diff options
Diffstat (limited to 'yocto-poky/meta/lib/oe/recipeutils.py')
-rw-r--r-- | yocto-poky/meta/lib/oe/recipeutils.py | 849 |
1 files changed, 0 insertions, 849 deletions
diff --git a/yocto-poky/meta/lib/oe/recipeutils.py b/yocto-poky/meta/lib/oe/recipeutils.py deleted file mode 100644 index 6c7adb5bd..000000000 --- a/yocto-poky/meta/lib/oe/recipeutils.py +++ /dev/null @@ -1,849 +0,0 @@ -# Utility functions for reading and modifying recipes -# -# Some code borrowed from the OE layer index -# -# Copyright (C) 2013-2015 Intel Corporation -# - -import sys -import os -import os.path -import tempfile -import textwrap -import difflib -import utils -import shutil -import re -import fnmatch -from collections import OrderedDict, defaultdict - - -# Help us to find places to insert values -recipe_progression = ['SUMMARY', 'DESCRIPTION', 'HOMEPAGE', 'BUGTRACKER', 'SECTION', 'LICENSE', 'LIC_FILES_CHKSUM', 'PROVIDES', 'DEPENDS', 'PR', 'PV', 'SRCREV', 'SRC_URI', 'S', 'do_fetch()', 'do_unpack()', 'do_patch()', 'EXTRA_OECONF', 'do_configure()', 'EXTRA_OEMAKE', 'do_compile()', 'do_install()', 'do_populate_sysroot()', 'INITSCRIPT', 'USERADD', 'GROUPADD', 'PACKAGES', 'FILES', 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RPROVIDES', 'RREPLACES', 'RCONFLICTS', 'ALLOW_EMPTY', 'do_package()', 'do_deploy()'] -# Variables that sometimes are a bit long but shouldn't be wrapped -nowrap_vars = ['SUMMARY', 'HOMEPAGE', 'BUGTRACKER', 'SRC_URI[md5sum]', 'SRC_URI[sha256sum]'] -list_vars = ['SRC_URI', 'LIC_FILES_CHKSUM'] -meta_vars = ['SUMMARY', 'DESCRIPTION', 'HOMEPAGE', 'BUGTRACKER', 'SECTION'] - - -def pn_to_recipe(cooker, pn): - """Convert a recipe name (PN) to the path to the recipe file""" - import bb.providers - - if pn in cooker.recipecache.pkg_pn: - best = bb.providers.findBestProvider(pn, cooker.data, cooker.recipecache, cooker.recipecache.pkg_pn) - return best[3] - elif pn in cooker.recipecache.providers: - filenames = cooker.recipecache.providers[pn] - eligible, foundUnique = bb.providers.filterProviders(filenames, pn, cooker.expanded_data, cooker.recipecache) - filename = eligible[0] - return filename - else: - return None - - -def get_unavailable_reasons(cooker, pn): - """If a recipe could not be found, find out why if possible""" - import bb.taskdata - taskdata = bb.taskdata.TaskData(None, skiplist=cooker.skiplist) - return taskdata.get_reasons(pn) - - -def parse_recipe(fn, appendfiles, d): - """ - Parse an individual recipe file, optionally with a list of - bbappend files. - """ - import bb.cache - envdata = bb.cache.Cache.loadDataFull(fn, appendfiles, d) - return envdata - - -def parse_recipe_simple(cooker, pn, d, appends=True): - """ - Parse a recipe and optionally all bbappends that apply to it - in the current configuration. - """ - import bb.providers - - recipefile = pn_to_recipe(cooker, pn) - if not recipefile: - skipreasons = get_unavailable_reasons(cooker, pn) - # We may as well re-use bb.providers.NoProvider here - if skipreasons: - raise bb.providers.NoProvider(skipreasons) - else: - raise bb.providers.NoProvider('Unable to find any recipe file matching %s' % pn) - if appends: - appendfiles = cooker.collection.get_file_appends(recipefile) - else: - appendfiles = None - return parse_recipe(recipefile, appendfiles, d) - - -def get_var_files(fn, varlist, d): - """Find the file in which each of a list of variables is set. - Note: requires variable history to be enabled when parsing. - """ - varfiles = {} - for v in varlist: - history = d.varhistory.variable(v) - files = [] - for event in history: - if 'file' in event and not 'flag' in event: - files.append(event['file']) - if files: - actualfile = files[-1] - else: - actualfile = None - varfiles[v] = actualfile - - return varfiles - - -def split_var_value(value, assignment=True): - """ - Split a space-separated variable's value into a list of items, - taking into account that some of the items might be made up of - expressions containing spaces that should not be split. - Parameters: - value: - The string value to split - assignment: - True to assume that the value represents an assignment - statement, False otherwise. If True, and an assignment - statement is passed in the first item in - the returned list will be the part of the assignment - statement up to and including the opening quote character, - and the last item will be the closing quote. - """ - inexpr = 0 - lastchar = None - out = [] - buf = '' - for char in value: - if char == '{': - if lastchar == '$': - inexpr += 1 - elif char == '}': - inexpr -= 1 - elif assignment and char in '"\'' and inexpr == 0: - if buf: - out.append(buf) - out.append(char) - char = '' - buf = '' - elif char.isspace() and inexpr == 0: - char = '' - if buf: - out.append(buf) - buf = '' - buf += char - lastchar = char - if buf: - out.append(buf) - - # Join together assignment statement and opening quote - outlist = out - if assignment: - assigfound = False - for idx, item in enumerate(out): - if '=' in item: - assigfound = True - if assigfound: - if '"' in item or "'" in item: - outlist = [' '.join(out[:idx+1])] - outlist.extend(out[idx+1:]) - break - return outlist - - -def patch_recipe_file(fn, values, patch=False, relpath=''): - """Update or insert variable values into a recipe file (assuming you - have already identified the exact file you want to update.) - Note that some manual inspection/intervention may be required - since this cannot handle all situations. - """ - - import bb.utils - - recipe_progression_res = [] - recipe_progression_restrs = [] - for item in recipe_progression: - if item.endswith('()'): - key = item[:-2] - else: - key = item - restr = '%s(_[a-zA-Z0-9-_$(){}]+|\[[^\]]*\])?' % key - if item.endswith('()'): - recipe_progression_restrs.append(restr + '()') - else: - recipe_progression_restrs.append(restr) - recipe_progression_res.append(re.compile('^%s$' % restr)) - - def get_recipe_pos(variable): - for i, p in enumerate(recipe_progression_res): - if p.match(variable): - return i - return -1 - - remainingnames = {} - for k in values.keys(): - remainingnames[k] = get_recipe_pos(k) - remainingnames = OrderedDict(sorted(remainingnames.iteritems(), key=lambda x: x[1])) - - modifying = False - - def outputvalue(name, lines, rewindcomments=False): - if values[name] is None: - return - rawtext = '%s = "%s"\n' % (name, values[name]) - addlines = [] - if name in nowrap_vars: - addlines.append(rawtext) - elif name in list_vars: - splitvalue = split_var_value(values[name], assignment=False) - if len(splitvalue) > 1: - linesplit = ' \\\n' + (' ' * (len(name) + 4)) - addlines.append('%s = "%s%s"\n' % (name, linesplit.join(splitvalue), linesplit)) - else: - addlines.append(rawtext) - else: - wrapped = textwrap.wrap(rawtext) - for wrapline in wrapped[:-1]: - addlines.append('%s \\\n' % wrapline) - addlines.append('%s\n' % wrapped[-1]) - if rewindcomments: - # Ensure we insert the lines before any leading comments - # (that we'd want to ensure remain leading the next value) - for i, ln in reversed(list(enumerate(lines))): - if ln[0] != '#': - lines[i+1:i+1] = addlines - break - else: - lines.extend(addlines) - else: - lines.extend(addlines) - - existingnames = [] - def patch_recipe_varfunc(varname, origvalue, op, newlines): - if modifying: - # Insert anything that should come before this variable - pos = get_recipe_pos(varname) - for k in remainingnames.keys()[:]: - if remainingnames[k] > -1 and pos >= remainingnames[k] and not k in existingnames: - outputvalue(k, newlines, rewindcomments=True) - del remainingnames[k] - # Now change this variable, if it needs to be changed - if varname in existingnames and op in ['+=', '=', '=+']: - if varname in remainingnames: - outputvalue(varname, newlines) - del remainingnames[varname] - return None, None, 0, True - else: - if varname in values: - existingnames.append(varname) - return origvalue, None, 0, True - - # First run - establish which values we want to set are already in the file - varlist = [re.escape(item) for item in values.keys()] - with open(fn, 'r') as f: - changed, fromlines = bb.utils.edit_metadata(f, varlist, patch_recipe_varfunc) - # Second run - actually set everything - modifying = True - varlist.extend(recipe_progression_restrs) - changed, tolines = bb.utils.edit_metadata(fromlines, varlist, patch_recipe_varfunc, match_overrides=True) - - if remainingnames: - if tolines[-1].strip() != '': - tolines.append('\n') - for k in remainingnames.keys(): - outputvalue(k, tolines) - - if patch: - relfn = os.path.relpath(fn, relpath) - diff = difflib.unified_diff(fromlines, tolines, 'a/%s' % relfn, 'b/%s' % relfn) - return diff - else: - with open(fn, 'w') as f: - f.writelines(tolines) - return None - - -def localise_file_vars(fn, varfiles, varlist): - """Given a list of variables and variable history (fetched with get_var_files()) - find where each variable should be set/changed. This handles for example where a - recipe includes an inc file where variables might be changed - in most cases - we want to update the inc file when changing the variable value rather than adding - it to the recipe itself. - """ - fndir = os.path.dirname(fn) + os.sep - - first_meta_file = None - for v in meta_vars: - f = varfiles.get(v, None) - if f: - actualdir = os.path.dirname(f) + os.sep - if actualdir.startswith(fndir): - first_meta_file = f - break - - filevars = defaultdict(list) - for v in varlist: - f = varfiles[v] - # Only return files that are in the same directory as the recipe or in some directory below there - # (this excludes bbclass files and common inc files that wouldn't be appropriate to set the variable - # in if we were going to set a value specific to this recipe) - if f: - actualfile = f - else: - # Variable isn't in a file, if it's one of the "meta" vars, use the first file with a meta var in it - if first_meta_file: - actualfile = first_meta_file - else: - actualfile = fn - - actualdir = os.path.dirname(actualfile) + os.sep - if not actualdir.startswith(fndir): - actualfile = fn - filevars[actualfile].append(v) - - return filevars - -def patch_recipe(d, fn, varvalues, patch=False, relpath=''): - """Modify a list of variable values in the specified recipe. Handles inc files if - used by the recipe. - """ - varlist = varvalues.keys() - varfiles = get_var_files(fn, varlist, d) - locs = localise_file_vars(fn, varfiles, varlist) - patches = [] - for f,v in locs.iteritems(): - vals = {k: varvalues[k] for k in v} - patchdata = patch_recipe_file(f, vals, patch, relpath) - if patch: - patches.append(patchdata) - - if patch: - return patches - else: - return None - - - -def copy_recipe_files(d, tgt_dir, whole_dir=False, download=True): - """Copy (local) recipe files, including both files included via include/require, - and files referred to in the SRC_URI variable.""" - import bb.fetch2 - import oe.path - - # FIXME need a warning if the unexpanded SRC_URI value contains variable references - - uris = (d.getVar('SRC_URI', True) or "").split() - fetch = bb.fetch2.Fetch(uris, d) - if download: - fetch.download() - - # Copy local files to target directory and gather any remote files - bb_dir = os.path.dirname(d.getVar('FILE', True)) + os.sep - remotes = [] - includes = [path for path in d.getVar('BBINCLUDED', True).split() if - path.startswith(bb_dir) and os.path.exists(path)] - for path in fetch.localpaths() + includes: - # Only import files that are under the meta directory - if path.startswith(bb_dir): - if not whole_dir: - relpath = os.path.relpath(path, bb_dir) - subdir = os.path.join(tgt_dir, os.path.dirname(relpath)) - if not os.path.exists(subdir): - os.makedirs(subdir) - shutil.copy2(path, os.path.join(tgt_dir, relpath)) - else: - remotes.append(path) - # Simply copy whole meta dir, if requested - if whole_dir: - shutil.copytree(bb_dir, tgt_dir) - - return remotes - - -def get_recipe_local_files(d, patches=False): - """Get a list of local files in SRC_URI within a recipe.""" - uris = (d.getVar('SRC_URI', True) or "").split() - fetch = bb.fetch2.Fetch(uris, d) - ret = {} - for uri in uris: - if fetch.ud[uri].type == 'file': - if (not patches and - bb.utils.exec_flat_python_func('patch_path', uri, fetch, '')): - continue - # Skip files that are referenced by absolute path - if not os.path.isabs(fetch.ud[uri].basepath): - ret[fetch.ud[uri].basepath] = fetch.localpath(uri) - return ret - - -def get_recipe_patches(d): - """Get a list of the patches included in SRC_URI within a recipe.""" - patchfiles = [] - # Execute src_patches() defined in patch.bbclass - this works since that class - # is inherited globally - patches = bb.utils.exec_flat_python_func('src_patches', d) - for patch in patches: - _, _, local, _, _, parm = bb.fetch.decodeurl(patch) - patchfiles.append(local) - return patchfiles - - -def get_recipe_patched_files(d): - """ - Get the list of patches for a recipe along with the files each patch modifies. - Params: - d: the datastore for the recipe - Returns: - a dict mapping patch file path to a list of tuples of changed files and - change mode ('A' for add, 'D' for delete or 'M' for modify) - """ - import oe.patch - # Execute src_patches() defined in patch.bbclass - this works since that class - # is inherited globally - patches = bb.utils.exec_flat_python_func('src_patches', d) - patchedfiles = {} - for patch in patches: - _, _, patchfile, _, _, parm = bb.fetch.decodeurl(patch) - striplevel = int(parm['striplevel']) - patchedfiles[patchfile] = oe.patch.PatchSet.getPatchedFiles(patchfile, striplevel, os.path.join(d.getVar('S', True), parm.get('patchdir', ''))) - return patchedfiles - - -def validate_pn(pn): - """Perform validation on a recipe name (PN) for a new recipe.""" - reserved_names = ['forcevariable', 'append', 'prepend', 'remove'] - if not re.match('[0-9a-z-.]+', pn): - return 'Recipe name "%s" is invalid: only characters 0-9, a-z, - and . are allowed' % pn - elif pn in reserved_names: - return 'Recipe name "%s" is invalid: is a reserved keyword' % pn - elif pn.startswith('pn-'): - return 'Recipe name "%s" is invalid: names starting with "pn-" are reserved' % pn - elif pn.endswith(('.bb', '.bbappend', '.bbclass', '.inc', '.conf')): - return 'Recipe name "%s" is invalid: should be just a name, not a file name' % pn - return '' - - -def get_bbappend_path(d, destlayerdir, wildcardver=False): - """Determine how a bbappend for a recipe should be named and located within another layer""" - - import bb.cookerdata - - destlayerdir = os.path.abspath(destlayerdir) - recipefile = d.getVar('FILE', True) - recipefn = os.path.splitext(os.path.basename(recipefile))[0] - if wildcardver and '_' in recipefn: - recipefn = recipefn.split('_', 1)[0] + '_%' - appendfn = recipefn + '.bbappend' - - # Parse the specified layer's layer.conf file directly, in case the layer isn't in bblayers.conf - confdata = d.createCopy() - confdata.setVar('BBFILES', '') - confdata.setVar('LAYERDIR', destlayerdir) - destlayerconf = os.path.join(destlayerdir, "conf", "layer.conf") - confdata = bb.cookerdata.parse_config_file(destlayerconf, confdata) - - origlayerdir = find_layerdir(recipefile) - if not origlayerdir: - return (None, False) - # Now join this to the path where the bbappend is going and check if it is covered by BBFILES - appendpath = os.path.join(destlayerdir, os.path.relpath(os.path.dirname(recipefile), origlayerdir), appendfn) - closepath = '' - pathok = True - for bbfilespec in confdata.getVar('BBFILES', True).split(): - if fnmatch.fnmatchcase(appendpath, bbfilespec): - # Our append path works, we're done - break - elif bbfilespec.startswith(destlayerdir) and fnmatch.fnmatchcase('test.bbappend', os.path.basename(bbfilespec)): - # Try to find the longest matching path - if len(bbfilespec) > len(closepath): - closepath = bbfilespec - else: - # Unfortunately the bbappend layer and the original recipe's layer don't have the same structure - if closepath: - # bbappend layer's layer.conf at least has a spec that picks up .bbappend files - # Now we just need to substitute out any wildcards - appendsubdir = os.path.relpath(os.path.dirname(closepath), destlayerdir) - if 'recipes-*' in appendsubdir: - # Try to copy this part from the original recipe path - res = re.search('/recipes-[^/]+/', recipefile) - if res: - appendsubdir = appendsubdir.replace('/recipes-*/', res.group(0)) - # This is crude, but we have to do something - appendsubdir = appendsubdir.replace('*', recipefn.split('_')[0]) - appendsubdir = appendsubdir.replace('?', 'a') - appendpath = os.path.join(destlayerdir, appendsubdir, appendfn) - else: - pathok = False - return (appendpath, pathok) - - -def bbappend_recipe(rd, destlayerdir, srcfiles, install=None, wildcardver=False, machine=None, extralines=None, removevalues=None): - """ - Writes a bbappend file for a recipe - Parameters: - rd: data dictionary for the recipe - destlayerdir: base directory of the layer to place the bbappend in - (subdirectory path from there will be determined automatically) - srcfiles: dict of source files to add to SRC_URI, where the value - is the full path to the file to be added, and the value is the - original filename as it would appear in SRC_URI or None if it - isn't already present. You may pass None for this parameter if - you simply want to specify your own content via the extralines - parameter. - install: dict mapping entries in srcfiles to a tuple of two elements: - install path (*without* ${D} prefix) and permission value (as a - string, e.g. '0644'). - wildcardver: True to use a % wildcard in the bbappend filename, or - False to make the bbappend specific to the recipe version. - machine: - If specified, make the changes in the bbappend specific to this - machine. This will also cause PACKAGE_ARCH = "${MACHINE_ARCH}" - to be added to the bbappend. - extralines: - Extra lines to add to the bbappend. This may be a dict of name - value pairs, or simply a list of the lines. - removevalues: - Variable values to remove - a dict of names/values. - """ - - if not removevalues: - removevalues = {} - - # Determine how the bbappend should be named - appendpath, pathok = get_bbappend_path(rd, destlayerdir, wildcardver) - if not appendpath: - bb.error('Unable to determine layer directory containing %s' % recipefile) - return (None, None) - if not pathok: - 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))) - - appenddir = os.path.dirname(appendpath) - bb.utils.mkdirhier(appenddir) - - # FIXME check if the bbappend doesn't get overridden by a higher priority layer? - - layerdirs = [os.path.abspath(layerdir) for layerdir in rd.getVar('BBLAYERS', True).split()] - if not os.path.abspath(destlayerdir) in layerdirs: - bb.warn('Specified layer is not currently enabled in bblayers.conf, you will need to add it before this bbappend will be active') - - bbappendlines = [] - if extralines: - if isinstance(extralines, dict): - for name, value in extralines.iteritems(): - bbappendlines.append((name, '=', value)) - else: - # Do our best to split it - for line in extralines: - if line[-1] == '\n': - line = line[:-1] - splitline = line.split(None, 2) - if len(splitline) == 3: - bbappendlines.append(tuple(splitline)) - else: - raise Exception('Invalid extralines value passed') - - def popline(varname): - for i in xrange(0, len(bbappendlines)): - if bbappendlines[i][0] == varname: - line = bbappendlines.pop(i) - return line - return None - - def appendline(varname, op, value): - for i in xrange(0, len(bbappendlines)): - item = bbappendlines[i] - if item[0] == varname: - bbappendlines[i] = (item[0], item[1], item[2] + ' ' + value) - break - else: - bbappendlines.append((varname, op, value)) - - destsubdir = rd.getVar('PN', True) - if srcfiles: - bbappendlines.append(('FILESEXTRAPATHS_prepend', ':=', '${THISDIR}/${PN}:')) - - appendoverride = '' - if machine: - bbappendlines.append(('PACKAGE_ARCH', '=', '${MACHINE_ARCH}')) - appendoverride = '_%s' % machine - copyfiles = {} - if srcfiles: - instfunclines = [] - for newfile, origsrcfile in srcfiles.iteritems(): - srcfile = origsrcfile - srcurientry = None - if not srcfile: - srcfile = os.path.basename(newfile) - srcurientry = 'file://%s' % srcfile - # Double-check it's not there already - # FIXME do we care if the entry is added by another bbappend that might go away? - if not srcurientry in rd.getVar('SRC_URI', True).split(): - if machine: - appendline('SRC_URI_append%s' % appendoverride, '=', ' ' + srcurientry) - else: - appendline('SRC_URI', '+=', srcurientry) - copyfiles[newfile] = srcfile - if install: - institem = install.pop(newfile, None) - if institem: - (destpath, perms) = institem - instdestpath = replace_dir_vars(destpath, rd) - instdirline = 'install -d ${D}%s' % os.path.dirname(instdestpath) - if not instdirline in instfunclines: - instfunclines.append(instdirline) - instfunclines.append('install -m %s ${WORKDIR}/%s ${D}%s' % (perms, os.path.basename(srcfile), instdestpath)) - if instfunclines: - bbappendlines.append(('do_install_append%s()' % appendoverride, '', instfunclines)) - - bb.note('Writing append file %s' % appendpath) - - if os.path.exists(appendpath): - # Work around lack of nonlocal in python 2 - extvars = {'destsubdir': destsubdir} - - def appendfile_varfunc(varname, origvalue, op, newlines): - if varname == 'FILESEXTRAPATHS_prepend': - if origvalue.startswith('${THISDIR}/'): - popline('FILESEXTRAPATHS_prepend') - extvars['destsubdir'] = rd.expand(origvalue.split('${THISDIR}/', 1)[1].rstrip(':')) - elif varname == 'PACKAGE_ARCH': - if machine: - popline('PACKAGE_ARCH') - return (machine, None, 4, False) - elif varname.startswith('do_install_append'): - func = popline(varname) - if func: - instfunclines = [line.strip() for line in origvalue.strip('\n').splitlines()] - for line in func[2]: - if not line in instfunclines: - instfunclines.append(line) - return (instfunclines, None, 4, False) - else: - splitval = split_var_value(origvalue, assignment=False) - changed = False - removevar = varname - if varname in ['SRC_URI', 'SRC_URI_append%s' % appendoverride]: - removevar = 'SRC_URI' - line = popline(varname) - if line: - if line[2] not in splitval: - splitval.append(line[2]) - changed = True - else: - line = popline(varname) - if line: - splitval = [line[2]] - changed = True - - if removevar in removevalues: - remove = removevalues[removevar] - if isinstance(remove, basestring): - if remove in splitval: - splitval.remove(remove) - changed = True - else: - for removeitem in remove: - if removeitem in splitval: - splitval.remove(removeitem) - changed = True - - if changed: - newvalue = splitval - if len(newvalue) == 1: - # Ensure it's written out as one line - if '_append' in varname: - newvalue = ' ' + newvalue[0] - else: - newvalue = newvalue[0] - if not newvalue and (op in ['+=', '.='] or '_append' in varname): - # There's no point appending nothing - newvalue = None - if varname.endswith('()'): - indent = 4 - else: - indent = -1 - return (newvalue, None, indent, True) - return (origvalue, None, 4, False) - - varnames = [item[0] for item in bbappendlines] - if removevalues: - varnames.extend(removevalues.keys()) - - with open(appendpath, 'r') as f: - (updated, newlines) = bb.utils.edit_metadata(f, varnames, appendfile_varfunc) - - destsubdir = extvars['destsubdir'] - else: - updated = False - newlines = [] - - if bbappendlines: - for line in bbappendlines: - if line[0].endswith('()'): - newlines.append('%s {\n %s\n}\n' % (line[0], '\n '.join(line[2]))) - else: - newlines.append('%s %s "%s"\n\n' % line) - updated = True - - if updated: - with open(appendpath, 'w') as f: - f.writelines(newlines) - - if copyfiles: - if machine: - destsubdir = os.path.join(destsubdir, machine) - for newfile, srcfile in copyfiles.iteritems(): - filedest = os.path.join(appenddir, destsubdir, os.path.basename(srcfile)) - if os.path.abspath(newfile) != os.path.abspath(filedest): - bb.note('Copying %s to %s' % (newfile, filedest)) - bb.utils.mkdirhier(os.path.dirname(filedest)) - shutil.copyfile(newfile, filedest) - - return (appendpath, os.path.join(appenddir, destsubdir)) - - -def find_layerdir(fn): - """ Figure out relative path to base of layer for a file (e.g. a recipe)""" - pth = os.path.dirname(fn) - layerdir = '' - while pth: - if os.path.exists(os.path.join(pth, 'conf', 'layer.conf')): - layerdir = pth - break - pth = os.path.dirname(pth) - return layerdir - - -def replace_dir_vars(path, d): - """Replace common directory paths with appropriate variable references (e.g. /etc becomes ${sysconfdir})""" - dirvars = {} - # Sort by length so we get the variables we're interested in first - for var in sorted(d.keys(), key=len): - if var.endswith('dir') and var.lower() == var: - value = d.getVar(var, True) - if value.startswith('/') and not '\n' in value and value not in dirvars: - dirvars[value] = var - for dirpath in sorted(dirvars.keys(), reverse=True): - path = path.replace(dirpath, '${%s}' % dirvars[dirpath]) - return path - -def get_recipe_pv_without_srcpv(pv, uri_type): - """ - Get PV without SRCPV common in SCM's for now only - support git. - - Returns tuple with pv, prefix and suffix. - """ - pfx = '' - sfx = '' - - if uri_type == 'git': - git_regex = re.compile("(?P<pfx>v?)(?P<ver>[^\+]*)((?P<sfx>\+(git)?r?(AUTOINC\+))(?P<rev>.*))?") - m = git_regex.match(pv) - - if m: - pv = m.group('ver') - pfx = m.group('pfx') - sfx = m.group('sfx') - else: - regex = re.compile("(?P<pfx>(v|r)?)(?P<ver>.*)") - m = regex.match(pv) - if m: - pv = m.group('ver') - pfx = m.group('pfx') - - return (pv, pfx, sfx) - -def get_recipe_upstream_version(rd): - """ - Get upstream version of recipe using bb.fetch2 methods with support for - http, https, ftp and git. - - bb.fetch2 exceptions can be raised, - FetchError when don't have network access or upstream site don't response. - NoMethodError when uri latest_versionstring method isn't implemented. - - Returns a dictonary with version, type and datetime. - Type can be A for Automatic, M for Manual and U for Unknown. - """ - from bb.fetch2 import decodeurl - from datetime import datetime - - ru = {} - ru['version'] = '' - ru['type'] = 'U' - ru['datetime'] = '' - - pv = rd.getVar('PV', True) - - # XXX: If don't have SRC_URI means that don't have upstream sources so - # returns the current recipe version, so that upstream version check - # declares a match. - src_uris = rd.getVar('SRC_URI', True) - if not src_uris: - ru['version'] = pv - ru['type'] = 'M' - ru['datetime'] = datetime.now() - return ru - - # XXX: we suppose that the first entry points to the upstream sources - src_uri = src_uris.split()[0] - uri_type, _, _, _, _, _ = decodeurl(src_uri) - - manual_upstream_version = rd.getVar("RECIPE_UPSTREAM_VERSION", True) - if manual_upstream_version: - # manual tracking of upstream version. - ru['version'] = manual_upstream_version - ru['type'] = 'M' - - manual_upstream_date = rd.getVar("CHECK_DATE", True) - if manual_upstream_date: - date = datetime.strptime(manual_upstream_date, "%b %d, %Y") - else: - date = datetime.now() - ru['datetime'] = date - - elif uri_type == "file": - # files are always up-to-date - ru['version'] = pv - ru['type'] = 'A' - ru['datetime'] = datetime.now() - else: - ud = bb.fetch2.FetchData(src_uri, rd) - pupver = ud.method.latest_versionstring(ud, rd) - (upversion, revision) = pupver - - # format git version version+gitAUTOINC+HASH - if uri_type == 'git': - (pv, pfx, sfx) = get_recipe_pv_without_srcpv(pv, uri_type) - - # if contains revision but not upversion use current pv - if upversion == '' and revision: - upversion = pv - - if upversion: - tmp = upversion - upversion = '' - - if pfx: - upversion = pfx + tmp - else: - upversion = tmp - - if sfx: - upversion = upversion + sfx + revision[:10] - - if upversion: - ru['version'] = upversion - ru['type'] = 'A' - - ru['datetime'] = datetime.now() - - return ru |