ConfHandler.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. """
  2. class for handling configuration data files
  3. Reads a .conf file and obtains its metadata
  4. """
  5. # Copyright (C) 2003, 2004 Chris Larson
  6. # Copyright (C) 2003, 2004 Phil Blundell
  7. #
  8. # SPDX-License-Identifier: GPL-2.0-only
  9. #
  10. import errno
  11. import re
  12. import os
  13. import bb.utils
  14. from bb.parse import ParseError, resolve_file, ast, logger, handle
  15. __config_regexp__ = re.compile( r"""
  16. ^
  17. (?P<exp>export\s+)?
  18. (?P<var>[a-zA-Z0-9\-_+.${}/~:]*?)
  19. (\[(?P<flag>[a-zA-Z0-9\-_+.][a-zA-Z0-9\-_+.@/]*)\])?
  20. \s* (
  21. (?P<colon>:=) |
  22. (?P<lazyques>\?\?=) |
  23. (?P<ques>\?=) |
  24. (?P<append>\+=) |
  25. (?P<prepend>=\+) |
  26. (?P<predot>=\.) |
  27. (?P<postdot>\.=) |
  28. =
  29. ) \s*
  30. (?!'[^']*'[^']*'$)
  31. (?!\"[^\"]*\"[^\"]*\"$)
  32. (?P<apo>['\"])
  33. (?P<value>.*)
  34. (?P=apo)
  35. $
  36. """, re.X)
  37. __include_regexp__ = re.compile( r"include\s+(.+)" )
  38. __require_regexp__ = re.compile( r"require\s+(.+)" )
  39. __includeall_regexp__ = re.compile( r"include_all\s+(.+)" )
  40. __export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/~]+)$" )
  41. __unset_regexp__ = re.compile( r"unset\s+([a-zA-Z0-9\-_+.${}/~]+)$" )
  42. __unset_flag_regexp__ = re.compile( r"unset\s+([a-zA-Z0-9\-_+.${}/~]+)\[([a-zA-Z0-9\-_+.][a-zA-Z0-9\-_+.@]+)\]$" )
  43. __addpylib_regexp__ = re.compile(r"addpylib\s+(.+)\s+(.+)" )
  44. __addfragments_regexp__ = re.compile(r"addfragments\s+(.+)\s+(.+)\s+(.+)" )
  45. def init(data):
  46. return
  47. def supports(fn, d):
  48. return fn[-5:] == ".conf"
  49. def include(parentfn, fns, lineno, data, error_out):
  50. """
  51. error_out: A string indicating the verb (e.g. "include", "inherit") to be
  52. used in a ParseError that will be raised if the file to be included could
  53. not be included. Specify False to avoid raising an error in this case.
  54. """
  55. fns = data.expand(fns)
  56. parentfn = data.expand(parentfn)
  57. # "include" or "require" accept zero to n space-separated file names to include.
  58. for fn in fns.split():
  59. include_single_file(parentfn, fn, lineno, data, error_out)
  60. def include_single_file(parentfn, fn, lineno, data, error_out):
  61. """
  62. Helper function for include() which does not expand or split its parameters.
  63. """
  64. if parentfn == fn: # prevent infinite recursion
  65. return None
  66. if not os.path.isabs(fn):
  67. dname = os.path.dirname(parentfn)
  68. bbpath = "%s:%s" % (dname, data.getVar("BBPATH"))
  69. abs_fn, attempts = bb.utils.which(bbpath, fn, history=True)
  70. if abs_fn and bb.parse.check_dependency(data, abs_fn):
  71. logger.warning("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE')))
  72. for af in attempts:
  73. bb.parse.mark_dependency(data, af)
  74. if abs_fn:
  75. fn = abs_fn
  76. elif bb.parse.check_dependency(data, fn):
  77. logger.warning("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE')))
  78. try:
  79. bb.parse.handle(fn, data, True)
  80. except (IOError, OSError) as exc:
  81. if exc.errno == errno.ENOENT:
  82. if error_out:
  83. raise ParseError("Could not %s file %s" % (error_out, fn), parentfn, lineno)
  84. logger.debug2("CONF file '%s' not found", fn)
  85. else:
  86. if error_out:
  87. raise ParseError("Could not %s file %s: %s" % (error_out, fn, exc.strerror), parentfn, lineno)
  88. else:
  89. raise ParseError("Error parsing %s: %s" % (fn, exc.strerror), parentfn, lineno)
  90. # We have an issue where a UI might want to enforce particular settings such as
  91. # an empty DISTRO variable. If configuration files do something like assigning
  92. # a weak default, it turns out to be very difficult to filter out these changes,
  93. # particularly when the weak default might appear half way though parsing a chain
  94. # of configuration files. We therefore let the UIs hook into configuration file
  95. # parsing. This turns out to be a hard problem to solve any other way.
  96. confFilters = []
  97. def handle(fn, data, include, baseconfig=False):
  98. init(data)
  99. if include == 0:
  100. oldfile = None
  101. else:
  102. oldfile = data.getVar('FILE', False)
  103. abs_fn = resolve_file(fn, data)
  104. with open(abs_fn, 'r') as f:
  105. statements = ast.StatementGroup()
  106. lineno = 0
  107. while True:
  108. lineno = lineno + 1
  109. s = f.readline()
  110. if not s:
  111. break
  112. origlineno = lineno
  113. origline = s
  114. w = s.strip()
  115. # skip empty lines
  116. if not w:
  117. continue
  118. s = s.rstrip()
  119. while s[-1] == '\\':
  120. line = f.readline()
  121. origline += line
  122. s2 = line.rstrip()
  123. lineno = lineno + 1
  124. if (not s2 or s2 and s2[0] != "#") and s[0] == "#" :
  125. bb.fatal("There is a confusing multiline, partially commented expression starting on line %s of file %s:\n%s\nPlease clarify whether this is all a comment or should be parsed." % (origlineno, fn, origline))
  126. s = s[:-1] + s2
  127. # skip comments
  128. if s[0] == '#':
  129. continue
  130. feeder(lineno, s, abs_fn, statements, baseconfig=baseconfig)
  131. # DONE WITH PARSING... time to evaluate
  132. data.setVar('FILE', abs_fn)
  133. statements.eval(data)
  134. if oldfile:
  135. data.setVar('FILE', oldfile)
  136. for f in confFilters:
  137. f(fn, data)
  138. return data
  139. # baseconfig is set for the bblayers/layer.conf cookerdata config parsing
  140. # The function is also used by BBHandler, conffile would be False
  141. def feeder(lineno, s, fn, statements, baseconfig=False, conffile=True):
  142. m = __config_regexp__.match(s)
  143. if m:
  144. groupd = m.groupdict()
  145. if groupd['var'] == "":
  146. raise ParseError("Empty variable name in assignment: '%s'" % s, fn, lineno);
  147. ast.handleData(statements, fn, lineno, groupd)
  148. return
  149. m = __include_regexp__.match(s)
  150. if m:
  151. ast.handleInclude(statements, fn, lineno, m, False)
  152. return
  153. m = __require_regexp__.match(s)
  154. if m:
  155. ast.handleInclude(statements, fn, lineno, m, True)
  156. return
  157. m = __includeall_regexp__.match(s)
  158. if m:
  159. ast.handleIncludeAll(statements, fn, lineno, m)
  160. return
  161. m = __export_regexp__.match(s)
  162. if m:
  163. ast.handleExport(statements, fn, lineno, m)
  164. return
  165. m = __unset_regexp__.match(s)
  166. if m:
  167. ast.handleUnset(statements, fn, lineno, m)
  168. return
  169. m = __unset_flag_regexp__.match(s)
  170. if m:
  171. ast.handleUnsetFlag(statements, fn, lineno, m)
  172. return
  173. m = __addpylib_regexp__.match(s)
  174. if baseconfig and conffile and m:
  175. ast.handlePyLib(statements, fn, lineno, m)
  176. return
  177. m = __addfragments_regexp__.match(s)
  178. if m:
  179. ast.handleAddFragments(statements, fn, lineno, m)
  180. return
  181. raise ParseError("unparsed line: '%s'" % s, fn, lineno);
  182. # Add us to the handlers list
  183. from bb.parse import handlers
  184. handlers.append({'supports': supports, 'handle': handle, 'init': init})
  185. del handlers