summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/utils.py
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2015-05-18 16:08:36 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-05-19 11:58:45 +0100
commitaefc80c02ed6d4de5768723d86ce80520387e1d3 (patch)
tree9606d0ddd49051ed0f83193c78ce5005225c89c4 /bitbake/lib/bb/utils.py
parentba0546bfaf23aa5ba1033e348a0a1addf0623abb (diff)
downloadpoky-aefc80c02ed6d4de5768723d86ce80520387e1d3.tar.gz
bitbake: lib/bb/utils: fix and extend edit_metadata_file()
Fix several bugs and add some useful enhancements to make this into a more generic metadata editing function: * Support modifying function values (name must be specified ending with "()") * Support dropping values by returning None as the new value * Split out edit_metadata() function to provide same functionality on a list/iterable * Pass operation to callback and allow function to return them * Pass current output lines to callback so they can be modified * Fix handling of single-quoted values * Handle :=, =+, .=, and =. operators * Support arbitrary indent string * Support indenting by length of assignment (by specifying -1) * Fix typo in variablename - intentspc -> indentspc * Expand function docstring to cover arguments / usage * Add a parameter to enable matching names with overrides applied * Add some bitbake-selftest tests Note that this does change the expected signature of the callback function. The only known caller is in lib/bb/utils.py itself; I doubt anyone else has made extensive use of this function yet. (Bitbake rev: 20059e4d5ab9bf0f32c781ccb208da3c95818018) Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/bb/utils.py')
-rw-r--r--bitbake/lib/bb/utils.py238
1 files changed, 180 insertions, 58 deletions
diff --git a/bitbake/lib/bb/utils.py b/bitbake/lib/bb/utils.py
index 0db7e56651..988b845a4a 100644
--- a/bitbake/lib/bb/utils.py
+++ b/bitbake/lib/bb/utils.py
@@ -963,14 +963,62 @@ def exec_flat_python_func(func, *args, **kwargs):
963 bb.utils.better_exec(comp, context, code, '<string>') 963 bb.utils.better_exec(comp, context, code, '<string>')
964 return context['retval'] 964 return context['retval']
965 965
966def edit_metadata_file(meta_file, variables, func): 966def edit_metadata(meta_lines, variables, varfunc, match_overrides=False):
967 """Edit a recipe or config file and modify one or more specified 967 """Edit lines from a recipe or config file and modify one or more
968 variable values set in the file using a specified callback function. 968 specified variable values set in the file using a specified callback
969 The file is only written to if the value(s) actually change. 969 function. Lines are expected to have trailing newlines.
970 Parameters:
971 meta_lines: lines from the file; can be a list or an iterable
972 (e.g. file pointer)
973 variables: a list of variable names to look for. Functions
974 may also be specified, but must be specified with '()' at
975 the end of the name. Note that the function doesn't have
976 any intrinsic understanding of _append, _prepend, _remove,
977 or overrides, so these are considered as part of the name.
978 These values go into a regular expression, so regular
979 expression syntax is allowed.
980 varfunc: callback function called for every variable matching
981 one of the entries in the variables parameter. The function
982 should take four arguments:
983 varname: name of variable matched
984 origvalue: current value in file
985 op: the operator (e.g. '+=')
986 newlines: list of lines up to this point. You can use
987 this to prepend lines before this variable setting
988 if you wish.
989 and should return a three-element tuple:
990 newvalue: new value to substitute in, or None to drop
991 the variable setting entirely. (If the removal
992 results in two consecutive blank lines, one of the
993 blank lines will also be dropped).
994 newop: the operator to use - if you specify None here,
995 the original operation will be used.
996 indent: number of spaces to indent multi-line entries,
997 or -1 to indent up to the level of the assignment
998 and opening quote, or a string to use as the indent.
999 minbreak: True to allow the first element of a
1000 multi-line value to continue on the same line as
1001 the assignment, False to indent before the first
1002 element.
1003 match_overrides: True to match items with _overrides on the end,
1004 False otherwise
1005 Returns a tuple:
1006 updated:
1007 True if changes were made, False otherwise.
1008 newlines:
1009 Lines after processing
970 """ 1010 """
1011
971 var_res = {} 1012 var_res = {}
1013 if match_overrides:
1014 override_re = '(_[a-zA-Z0-9-_$(){}]+)?'
1015 else:
1016 override_re = ''
972 for var in variables: 1017 for var in variables:
973 var_res[var] = re.compile(r'^%s[ \t]*[?+]*=' % var) 1018 if var.endswith('()'):
1019 var_res[var] = re.compile('^(%s%s)[ \\t]*\([ \\t]*\)[ \\t]*{' % (var[:-2].rstrip(), override_re))
1020 else:
1021 var_res[var] = re.compile('^(%s%s)[ \\t]*[?+:.]*=[+.]*[ \\t]*(["\'])' % (var, override_re))
974 1022
975 updated = False 1023 updated = False
976 varset_start = '' 1024 varset_start = ''
@@ -978,70 +1026,144 @@ def edit_metadata_file(meta_file, variables, func):
978 newlines = [] 1026 newlines = []
979 in_var = None 1027 in_var = None
980 full_value = '' 1028 full_value = ''
1029 var_end = ''
981 1030
982 def handle_var_end(): 1031 def handle_var_end():
983 (newvalue, indent, minbreak) = func(in_var, full_value) 1032 prerun_newlines = newlines[:]
984 if newvalue != full_value: 1033 op = varset_start[len(in_var):].strip()
985 if isinstance(newvalue, list): 1034 (newvalue, newop, indent, minbreak) = varfunc(in_var, full_value, op, newlines)
986 intentspc = ' ' * indent 1035 changed = (prerun_newlines != newlines)
987 if minbreak: 1036
988 # First item on first line 1037 if newvalue is None:
989 if len(newvalue) == 1: 1038 # Drop the value
990 newlines.append('%s "%s"\n' % (varset_start, newvalue[0])) 1039 return True
1040 elif newvalue != full_value or (newop not in [None, op]):
1041 if newop not in [None, op]:
1042 # Callback changed the operator
1043 varset_new = "%s %s" % (in_var, newop)
1044 else:
1045 varset_new = varset_start
1046
1047 if isinstance(indent, (int, long)):
1048 if indent == -1:
1049 indentspc = ' ' * (len(varset_new) + 2)
1050 else:
1051 indentspc = ' ' * indent
1052 else:
1053 indentspc = indent
1054 if in_var.endswith('()'):
1055 # A function definition
1056 if isinstance(newvalue, list):
1057 newlines.append('%s {\n%s%s\n}\n' % (varset_new, indentspc, ('\n%s' % indentspc).join(newvalue)))
1058 else:
1059 if not newvalue.startswith('\n'):
1060 newvalue = '\n' + newvalue
1061 if not newvalue.endswith('\n'):
1062 newvalue = newvalue + '\n'
1063 newlines.append('%s {%s}\n' % (varset_new, newvalue))
1064 else:
1065 # Normal variable
1066 if isinstance(newvalue, list):
1067 if not newvalue:
1068 # Empty list -> empty string
1069 newlines.append('%s ""\n' % varset_new)
1070 elif minbreak:
1071 # First item on first line
1072 if len(newvalue) == 1:
1073 newlines.append('%s "%s"\n' % (varset_new, newvalue[0]))
1074 else:
1075 newlines.append('%s "%s \\\n' % (varset_new, newvalue[0]))
1076 for item in newvalue[1:]:
1077 newlines.append('%s%s \\\n' % (indentspc, item))
1078 newlines.append('%s"\n' % indentspc)
991 else: 1079 else:
992 newlines.append('%s "%s\\\n' % (varset_start, newvalue[0])) 1080 # No item on first line
993 for item in newvalue[1:]: 1081 newlines.append('%s " \\\n' % varset_new)
994 newlines.append('%s%s \\\n' % (intentspc, item)) 1082 for item in newvalue:
1083 newlines.append('%s%s \\\n' % (indentspc, item))
995 newlines.append('%s"\n' % indentspc) 1084 newlines.append('%s"\n' % indentspc)
996 else: 1085 else:
997 # No item on first line 1086 newlines.append('%s "%s"\n' % (varset_new, newvalue))
998 newlines.append('%s " \\\n' % varset_start)
999 for item in newvalue:
1000 newlines.append('%s%s \\\n' % (intentspc, item))
1001 newlines.append('%s"\n' % intentspc)
1002 else:
1003 newlines.append('%s "%s"\n' % (varset_start, newvalue))
1004 return True 1087 return True
1005 else: 1088 else:
1006 # Put the old lines back where they were 1089 # Put the old lines back where they were
1007 newlines.extend(varlines) 1090 newlines.extend(varlines)
1008 return False 1091 # If newlines was touched by the function, we'll need to return True
1092 return changed
1009 1093
1010 with open(meta_file, 'r') as f: 1094 checkspc = False
1011 for line in f: 1095
1012 if in_var: 1096 for line in meta_lines:
1013 value = line.rstrip() 1097 if in_var:
1014 varlines.append(line) 1098 value = line.rstrip()
1015 full_value += value[:-1] 1099 varlines.append(line)
1016 if value.endswith('"') or value.endswith("'"): 1100 if in_var.endswith('()'):
1017 full_value = full_value[:-1] 1101 full_value += '\n' + value
1018 if handle_var_end():
1019 updated = True
1020 in_var = None
1021 else: 1102 else:
1022 matched = False 1103 full_value += value[:-1]
1023 for (varname, var_re) in var_res.iteritems(): 1104 if value.endswith(var_end):
1024 if var_re.match(line): 1105 if in_var.endswith('()'):
1025 splitvalue = line.split('"', 1) 1106 if full_value.count('{') - full_value.count('}') >= 0:
1026 varset_start = splitvalue[0].rstrip() 1107 continue
1027 value = splitvalue[1].rstrip() 1108 full_value = full_value[:-1]
1028 if value.endswith('\\'): 1109 if handle_var_end():
1029 value = value[:-1] 1110 updated = True
1030 full_value = value 1111 checkspc = True
1031 varlines = [line] 1112 in_var = None
1032 in_var = varname 1113 else:
1033 if value.endswith('"') or value.endswith("'"): 1114 skip = False
1034 full_value = full_value[:-1] 1115 for (varname, var_re) in var_res.iteritems():
1035 if handle_var_end(): 1116 res = var_re.match(line)
1036 updated = True 1117 if res:
1037 in_var = None 1118 isfunc = varname.endswith('()')
1038 matched = True 1119 if isfunc:
1039 break 1120 splitvalue = line.split('{', 1)
1040 if not matched: 1121 var_end = '}'
1041 newlines.append(line) 1122 else:
1123 var_end = res.groups()[-1]
1124 splitvalue = line.split(var_end, 1)
1125 varset_start = splitvalue[0].rstrip()
1126 value = splitvalue[1].rstrip()
1127 if not isfunc and value.endswith('\\'):
1128 value = value[:-1]
1129 full_value = value
1130 varlines = [line]
1131 in_var = res.group(1)
1132 if isfunc:
1133 in_var += '()'
1134 if value.endswith(var_end):
1135 full_value = full_value[:-1]
1136 if handle_var_end():
1137 updated = True
1138 checkspc = True
1139 in_var = None
1140 skip = True
1141 break
1142 if not skip:
1143 if checkspc:
1144 checkspc = False
1145 if newlines[-1] == '\n' and line == '\n':
1146 # Squash blank line if there are two consecutive blanks after a removal
1147 continue
1148 newlines.append(line)
1149 return (updated, newlines)
1150
1151
1152def edit_metadata_file(meta_file, variables, varfunc):
1153 """Edit a recipe or config file and modify one or more specified
1154 variable values set in the file using a specified callback function.
1155 The file is only written to if the value(s) actually change.
1156 This is basically the file version of edit_metadata(), see that
1157 function's description for parameter/usage information.
1158 Returns True if the file was written to, False otherwise.
1159 """
1160 with open(meta_file, 'r') as f:
1161 (updated, newlines) = edit_metadata(f, variables, varfunc)
1042 if updated: 1162 if updated:
1043 with open(meta_file, 'w') as f: 1163 with open(meta_file, 'w') as f:
1044 f.writelines(newlines) 1164 f.writelines(newlines)
1165 return updated
1166
1045 1167
1046def edit_bblayers_conf(bblayers_conf, add, remove): 1168def edit_bblayers_conf(bblayers_conf, add, remove):
1047 """Edit bblayers.conf, adding and/or removing layers""" 1169 """Edit bblayers.conf, adding and/or removing layers"""
@@ -1070,7 +1192,7 @@ def edit_bblayers_conf(bblayers_conf, add, remove):
1070 # Need to use a list here because we can't set non-local variables from a callback in python 2.x 1192 # Need to use a list here because we can't set non-local variables from a callback in python 2.x
1071 bblayercalls = [] 1193 bblayercalls = []
1072 1194
1073 def handle_bblayers(varname, origvalue): 1195 def handle_bblayers(varname, origvalue, op, newlines):
1074 bblayercalls.append(varname) 1196 bblayercalls.append(varname)
1075 updated = False 1197 updated = False
1076 bblayers = [remove_trailing_sep(x) for x in origvalue.split()] 1198 bblayers = [remove_trailing_sep(x) for x in origvalue.split()]
@@ -1094,9 +1216,9 @@ def edit_bblayers_conf(bblayers_conf, add, remove):
1094 notadded.append(addlayer) 1216 notadded.append(addlayer)
1095 1217
1096 if updated: 1218 if updated:
1097 return (bblayers, 2, False) 1219 return (bblayers, None, 2, False)
1098 else: 1220 else:
1099 return (origvalue, 2, False) 1221 return (origvalue, None, 2, False)
1100 1222
1101 edit_metadata_file(bblayers_conf, ['BBLAYERS'], handle_bblayers) 1223 edit_metadata_file(bblayers_conf, ['BBLAYERS'], handle_bblayers)
1102 1224