1*4882a593Smuzhiyun""" 2*4882a593Smuzhiyun class for handling .bb files 3*4882a593Smuzhiyun 4*4882a593Smuzhiyun Reads a .bb file and obtains its metadata 5*4882a593Smuzhiyun 6*4882a593Smuzhiyun""" 7*4882a593Smuzhiyun 8*4882a593Smuzhiyun 9*4882a593Smuzhiyun# Copyright (C) 2003, 2004 Chris Larson 10*4882a593Smuzhiyun# Copyright (C) 2003, 2004 Phil Blundell 11*4882a593Smuzhiyun# 12*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 13*4882a593Smuzhiyun# 14*4882a593Smuzhiyun 15*4882a593Smuzhiyunimport re, bb, os 16*4882a593Smuzhiyunimport bb.build, bb.utils, bb.data_smart 17*4882a593Smuzhiyun 18*4882a593Smuzhiyunfrom . import ConfHandler 19*4882a593Smuzhiyunfrom .. import resolve_file, ast, logger, ParseError 20*4882a593Smuzhiyunfrom .ConfHandler import include, init 21*4882a593Smuzhiyun 22*4882a593Smuzhiyun__func_start_regexp__ = re.compile(r"(((?P<py>python(?=(\s|\()))|(?P<fr>fakeroot(?=\s)))\s*)*(?P<func>[\w\.\-\+\{\}\$:]+)?\s*\(\s*\)\s*{$" ) 23*4882a593Smuzhiyun__inherit_regexp__ = re.compile(r"inherit\s+(.+)" ) 24*4882a593Smuzhiyun__export_func_regexp__ = re.compile(r"EXPORT_FUNCTIONS\s+(.+)" ) 25*4882a593Smuzhiyun__addtask_regexp__ = re.compile(r"addtask\s+(?P<func>\w+)\s*((before\s*(?P<before>((.*(?=after))|(.*))))|(after\s*(?P<after>((.*(?=before))|(.*)))))*") 26*4882a593Smuzhiyun__deltask_regexp__ = re.compile(r"deltask\s+(.+)") 27*4882a593Smuzhiyun__addhandler_regexp__ = re.compile(r"addhandler\s+(.+)" ) 28*4882a593Smuzhiyun__def_regexp__ = re.compile(r"def\s+(\w+).*:" ) 29*4882a593Smuzhiyun__python_func_regexp__ = re.compile(r"(\s+.*)|(^$)|(^#)" ) 30*4882a593Smuzhiyun__python_tab_regexp__ = re.compile(r" *\t") 31*4882a593Smuzhiyun 32*4882a593Smuzhiyun__infunc__ = [] 33*4882a593Smuzhiyun__inpython__ = False 34*4882a593Smuzhiyun__body__ = [] 35*4882a593Smuzhiyun__classname__ = "" 36*4882a593Smuzhiyun 37*4882a593Smuzhiyuncached_statements = {} 38*4882a593Smuzhiyun 39*4882a593Smuzhiyundef supports(fn, d): 40*4882a593Smuzhiyun """Return True if fn has a supported extension""" 41*4882a593Smuzhiyun return os.path.splitext(fn)[-1] in [".bb", ".bbclass", ".inc"] 42*4882a593Smuzhiyun 43*4882a593Smuzhiyundef inherit(files, fn, lineno, d): 44*4882a593Smuzhiyun __inherit_cache = d.getVar('__inherit_cache', False) or [] 45*4882a593Smuzhiyun files = d.expand(files).split() 46*4882a593Smuzhiyun for file in files: 47*4882a593Smuzhiyun if not os.path.isabs(file) and not file.endswith(".bbclass"): 48*4882a593Smuzhiyun file = os.path.join('classes', '%s.bbclass' % file) 49*4882a593Smuzhiyun 50*4882a593Smuzhiyun if not os.path.isabs(file): 51*4882a593Smuzhiyun bbpath = d.getVar("BBPATH") 52*4882a593Smuzhiyun abs_fn, attempts = bb.utils.which(bbpath, file, history=True) 53*4882a593Smuzhiyun for af in attempts: 54*4882a593Smuzhiyun if af != abs_fn: 55*4882a593Smuzhiyun bb.parse.mark_dependency(d, af) 56*4882a593Smuzhiyun if abs_fn: 57*4882a593Smuzhiyun file = abs_fn 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun if not file in __inherit_cache: 60*4882a593Smuzhiyun logger.debug("Inheriting %s (from %s:%d)" % (file, fn, lineno)) 61*4882a593Smuzhiyun __inherit_cache.append( file ) 62*4882a593Smuzhiyun d.setVar('__inherit_cache', __inherit_cache) 63*4882a593Smuzhiyun include(fn, file, lineno, d, "inherit") 64*4882a593Smuzhiyun __inherit_cache = d.getVar('__inherit_cache', False) or [] 65*4882a593Smuzhiyun 66*4882a593Smuzhiyundef get_statements(filename, absolute_filename, base_name): 67*4882a593Smuzhiyun global cached_statements 68*4882a593Smuzhiyun 69*4882a593Smuzhiyun try: 70*4882a593Smuzhiyun return cached_statements[absolute_filename] 71*4882a593Smuzhiyun except KeyError: 72*4882a593Smuzhiyun with open(absolute_filename, 'r') as f: 73*4882a593Smuzhiyun statements = ast.StatementGroup() 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun lineno = 0 76*4882a593Smuzhiyun while True: 77*4882a593Smuzhiyun lineno = lineno + 1 78*4882a593Smuzhiyun s = f.readline() 79*4882a593Smuzhiyun if not s: break 80*4882a593Smuzhiyun s = s.rstrip() 81*4882a593Smuzhiyun feeder(lineno, s, filename, base_name, statements) 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun if __inpython__: 84*4882a593Smuzhiyun # add a blank line to close out any python definition 85*4882a593Smuzhiyun feeder(lineno, "", filename, base_name, statements, eof=True) 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun if filename.endswith(".bbclass") or filename.endswith(".inc"): 88*4882a593Smuzhiyun cached_statements[absolute_filename] = statements 89*4882a593Smuzhiyun return statements 90*4882a593Smuzhiyun 91*4882a593Smuzhiyundef handle(fn, d, include): 92*4882a593Smuzhiyun global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __infunc__, __body__, __residue__, __classname__ 93*4882a593Smuzhiyun __body__ = [] 94*4882a593Smuzhiyun __infunc__ = [] 95*4882a593Smuzhiyun __classname__ = "" 96*4882a593Smuzhiyun __residue__ = [] 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun base_name = os.path.basename(fn) 99*4882a593Smuzhiyun (root, ext) = os.path.splitext(base_name) 100*4882a593Smuzhiyun init(d) 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun if ext == ".bbclass": 103*4882a593Smuzhiyun __classname__ = root 104*4882a593Smuzhiyun __inherit_cache = d.getVar('__inherit_cache', False) or [] 105*4882a593Smuzhiyun if not fn in __inherit_cache: 106*4882a593Smuzhiyun __inherit_cache.append(fn) 107*4882a593Smuzhiyun d.setVar('__inherit_cache', __inherit_cache) 108*4882a593Smuzhiyun 109*4882a593Smuzhiyun if include != 0: 110*4882a593Smuzhiyun oldfile = d.getVar('FILE', False) 111*4882a593Smuzhiyun else: 112*4882a593Smuzhiyun oldfile = None 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun abs_fn = resolve_file(fn, d) 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun # actual loading 117*4882a593Smuzhiyun statements = get_statements(fn, abs_fn, base_name) 118*4882a593Smuzhiyun 119*4882a593Smuzhiyun # DONE WITH PARSING... time to evaluate 120*4882a593Smuzhiyun if ext != ".bbclass" and abs_fn != oldfile: 121*4882a593Smuzhiyun d.setVar('FILE', abs_fn) 122*4882a593Smuzhiyun 123*4882a593Smuzhiyun try: 124*4882a593Smuzhiyun statements.eval(d) 125*4882a593Smuzhiyun except bb.parse.SkipRecipe: 126*4882a593Smuzhiyun d.setVar("__SKIPPED", True) 127*4882a593Smuzhiyun if include == 0: 128*4882a593Smuzhiyun return { "" : d } 129*4882a593Smuzhiyun 130*4882a593Smuzhiyun if __infunc__: 131*4882a593Smuzhiyun raise ParseError("Shell function %s is never closed" % __infunc__[0], __infunc__[1], __infunc__[2]) 132*4882a593Smuzhiyun if __residue__: 133*4882a593Smuzhiyun raise ParseError("Leftover unparsed (incomplete?) data %s from %s" % __residue__, fn) 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun if ext != ".bbclass" and include == 0: 136*4882a593Smuzhiyun return ast.multi_finalize(fn, d) 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun if ext != ".bbclass" and oldfile and abs_fn != oldfile: 139*4882a593Smuzhiyun d.setVar("FILE", oldfile) 140*4882a593Smuzhiyun 141*4882a593Smuzhiyun return d 142*4882a593Smuzhiyun 143*4882a593Smuzhiyundef feeder(lineno, s, fn, root, statements, eof=False): 144*4882a593Smuzhiyun global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__, __infunc__, __body__, bb, __residue__, __classname__ 145*4882a593Smuzhiyun 146*4882a593Smuzhiyun # Check tabs in python functions: 147*4882a593Smuzhiyun # - def py_funcname(): covered by __inpython__ 148*4882a593Smuzhiyun # - python(): covered by '__anonymous' == __infunc__[0] 149*4882a593Smuzhiyun # - python funcname(): covered by __infunc__[3] 150*4882a593Smuzhiyun if __inpython__ or (__infunc__ and ('__anonymous' == __infunc__[0] or __infunc__[3])): 151*4882a593Smuzhiyun tab = __python_tab_regexp__.match(s) 152*4882a593Smuzhiyun if tab: 153*4882a593Smuzhiyun bb.warn('python should use 4 spaces indentation, but found tabs in %s, line %s' % (root, lineno)) 154*4882a593Smuzhiyun 155*4882a593Smuzhiyun if __infunc__: 156*4882a593Smuzhiyun if s == '}': 157*4882a593Smuzhiyun __body__.append('') 158*4882a593Smuzhiyun ast.handleMethod(statements, fn, lineno, __infunc__[0], __body__, __infunc__[3], __infunc__[4]) 159*4882a593Smuzhiyun __infunc__ = [] 160*4882a593Smuzhiyun __body__ = [] 161*4882a593Smuzhiyun else: 162*4882a593Smuzhiyun __body__.append(s) 163*4882a593Smuzhiyun return 164*4882a593Smuzhiyun 165*4882a593Smuzhiyun if __inpython__: 166*4882a593Smuzhiyun m = __python_func_regexp__.match(s) 167*4882a593Smuzhiyun if m and not eof: 168*4882a593Smuzhiyun __body__.append(s) 169*4882a593Smuzhiyun return 170*4882a593Smuzhiyun else: 171*4882a593Smuzhiyun ast.handlePythonMethod(statements, fn, lineno, __inpython__, 172*4882a593Smuzhiyun root, __body__) 173*4882a593Smuzhiyun __body__ = [] 174*4882a593Smuzhiyun __inpython__ = False 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun if eof: 177*4882a593Smuzhiyun return 178*4882a593Smuzhiyun 179*4882a593Smuzhiyun if s and s[0] == '#': 180*4882a593Smuzhiyun if len(__residue__) != 0 and __residue__[0][0] != "#": 181*4882a593Smuzhiyun bb.fatal("There is a comment on line %s of file %s:\n'''\n%s\n'''\nwhich is in the middle of a multiline expression. This syntax is invalid, please correct it." % (lineno, fn, s)) 182*4882a593Smuzhiyun 183*4882a593Smuzhiyun if len(__residue__) != 0 and __residue__[0][0] == "#" and (not s or s[0] != "#"): 184*4882a593Smuzhiyun bb.fatal("There is a confusing multiline partially commented expression on line %s of file %s:\n%s\nPlease clarify whether this is all a comment or should be parsed." % (lineno - len(__residue__), fn, "\n".join(__residue__))) 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun if s and s[-1] == '\\': 187*4882a593Smuzhiyun __residue__.append(s[:-1]) 188*4882a593Smuzhiyun return 189*4882a593Smuzhiyun 190*4882a593Smuzhiyun s = "".join(__residue__) + s 191*4882a593Smuzhiyun __residue__ = [] 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun # Skip empty lines 194*4882a593Smuzhiyun if s == '': 195*4882a593Smuzhiyun return 196*4882a593Smuzhiyun 197*4882a593Smuzhiyun # Skip comments 198*4882a593Smuzhiyun if s[0] == '#': 199*4882a593Smuzhiyun return 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun m = __func_start_regexp__.match(s) 202*4882a593Smuzhiyun if m: 203*4882a593Smuzhiyun __infunc__ = [m.group("func") or "__anonymous", fn, lineno, m.group("py") is not None, m.group("fr") is not None] 204*4882a593Smuzhiyun return 205*4882a593Smuzhiyun 206*4882a593Smuzhiyun m = __def_regexp__.match(s) 207*4882a593Smuzhiyun if m: 208*4882a593Smuzhiyun __body__.append(s) 209*4882a593Smuzhiyun __inpython__ = m.group(1) 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun return 212*4882a593Smuzhiyun 213*4882a593Smuzhiyun m = __export_func_regexp__.match(s) 214*4882a593Smuzhiyun if m: 215*4882a593Smuzhiyun ast.handleExportFuncs(statements, fn, lineno, m, __classname__) 216*4882a593Smuzhiyun return 217*4882a593Smuzhiyun 218*4882a593Smuzhiyun m = __addtask_regexp__.match(s) 219*4882a593Smuzhiyun if m: 220*4882a593Smuzhiyun if len(m.group().split()) == 2: 221*4882a593Smuzhiyun # Check and warn for "addtask task1 task2" 222*4882a593Smuzhiyun m2 = re.match(r"addtask\s+(?P<func>\w+)(?P<ignores>.*)", s) 223*4882a593Smuzhiyun if m2 and m2.group('ignores'): 224*4882a593Smuzhiyun logger.warning('addtask ignored: "%s"' % m2.group('ignores')) 225*4882a593Smuzhiyun 226*4882a593Smuzhiyun # Check and warn for "addtask task1 before task2 before task3", the 227*4882a593Smuzhiyun # similar to "after" 228*4882a593Smuzhiyun taskexpression = s.split() 229*4882a593Smuzhiyun for word in ('before', 'after'): 230*4882a593Smuzhiyun if taskexpression.count(word) > 1: 231*4882a593Smuzhiyun logger.warning("addtask contained multiple '%s' keywords, only one is supported" % word) 232*4882a593Smuzhiyun 233*4882a593Smuzhiyun # Check and warn for having task with exprssion as part of task name 234*4882a593Smuzhiyun for te in taskexpression: 235*4882a593Smuzhiyun if any( ( "%s_" % keyword ) in te for keyword in bb.data_smart.__setvar_keyword__ ): 236*4882a593Smuzhiyun raise ParseError("Task name '%s' contains a keyword which is not recommended/supported.\nPlease rename the task not to include the keyword.\n%s" % (te, ("\n".join(map(str, bb.data_smart.__setvar_keyword__)))), fn) 237*4882a593Smuzhiyun ast.handleAddTask(statements, fn, lineno, m) 238*4882a593Smuzhiyun return 239*4882a593Smuzhiyun 240*4882a593Smuzhiyun m = __deltask_regexp__.match(s) 241*4882a593Smuzhiyun if m: 242*4882a593Smuzhiyun ast.handleDelTask(statements, fn, lineno, m) 243*4882a593Smuzhiyun return 244*4882a593Smuzhiyun 245*4882a593Smuzhiyun m = __addhandler_regexp__.match(s) 246*4882a593Smuzhiyun if m: 247*4882a593Smuzhiyun ast.handleBBHandlers(statements, fn, lineno, m) 248*4882a593Smuzhiyun return 249*4882a593Smuzhiyun 250*4882a593Smuzhiyun m = __inherit_regexp__.match(s) 251*4882a593Smuzhiyun if m: 252*4882a593Smuzhiyun ast.handleInherit(statements, fn, lineno, m) 253*4882a593Smuzhiyun return 254*4882a593Smuzhiyun 255*4882a593Smuzhiyun return ConfHandler.feeder(lineno, s, fn, statements) 256*4882a593Smuzhiyun 257*4882a593Smuzhiyun# Add us to the handlers list 258*4882a593Smuzhiyunfrom .. import handlers 259*4882a593Smuzhiyunhandlers.append({'supports': supports, 'handle': handle, 'init': init}) 260*4882a593Smuzhiyundel handlers 261