kconfig.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #!/usr/bin/env python3
  2. # Writes/updates the zephyr/.config configuration file by merging configuration
  3. # files passed as arguments, e.g. board *_defconfig and application prj.conf
  4. # files.
  5. #
  6. # When fragments haven't changed, zephyr/.config is both the input and the
  7. # output, which just updates it. This is handled in the CMake files.
  8. #
  9. # Also does various checks (most via Kconfiglib warnings).
  10. import argparse
  11. import os
  12. import sys
  13. import textwrap
  14. # Zephyr doesn't use tristate symbols. They're supported here just to make the
  15. # script a bit more generic.
  16. from kconfiglib import Kconfig, split_expr, expr_value, expr_str, BOOL, \
  17. TRISTATE, TRI_TO_STR, AND, OR
  18. def main():
  19. args = parse_args()
  20. if args.zephyr_base:
  21. os.environ['ZEPHYR_BASE'] = args.zephyr_base
  22. print("Parsing " + args.kconfig_file)
  23. kconf = Kconfig(args.kconfig_file, warn_to_stderr=False,
  24. suppress_traceback=True)
  25. if args.handwritten_input_configs:
  26. # Warn for assignments to undefined symbols, but only for handwritten
  27. # fragments, to avoid warnings-turned-errors when using an old
  28. # configuration file together with updated Kconfig files
  29. kconf.warn_assign_undef = True
  30. # prj.conf may override settings from the board configuration, so
  31. # disable warnings about symbols being assigned more than once
  32. kconf.warn_assign_override = False
  33. kconf.warn_assign_redun = False
  34. # Load configuration files
  35. print(kconf.load_config(args.configs_in[0]))
  36. for config in args.configs_in[1:]:
  37. # replace=False creates a merged configuration
  38. print(kconf.load_config(config, replace=False))
  39. if args.handwritten_input_configs:
  40. # Check that there are no assignments to promptless symbols, which
  41. # have no effect.
  42. #
  43. # This only makes sense when loading handwritten fragments and not when
  44. # loading zephyr/.config, because zephyr/.config is configuration
  45. # output and also assigns promptless symbols.
  46. check_no_promptless_assign(kconf)
  47. # Print warnings for symbols that didn't get the assigned value. Only
  48. # do this for handwritten input too, to avoid likely unhelpful warnings
  49. # when using an old configuration and updating Kconfig files.
  50. check_assigned_sym_values(kconf)
  51. check_assigned_choice_values(kconf)
  52. #if kconf.syms['WARN_DEPRECATED'].tri_value == 2:
  53. # check_deprecated(kconf)
  54. #if kconf.syms['WARN_EXPERIMENTAL'].tri_value == 2:
  55. # check_experimental(kconf)
  56. # Hack: Force all symbols to be evaluated, to catch warnings generated
  57. # during evaluation. Wait till the end to write the actual output files, so
  58. # that we don't generate any output if there are warnings-turned-errors.
  59. #
  60. # Kconfiglib caches calculated symbol values internally, so this is still
  61. # fast.
  62. kconf.write_config(os.devnull)
  63. if kconf.warnings:
  64. # Put a blank line between warnings to make them easier to read
  65. for warning in kconf.warnings:
  66. print("\n" + warning, file=sys.stderr)
  67. # Turn all warnings into errors, so that e.g. assignments to undefined
  68. # Kconfig symbols become errors.
  69. #
  70. # A warning is generated by this script whenever a symbol gets a
  71. # different value than the one it was assigned. Keep that one as just a
  72. # warning for now.
  73. err("Aborting due to Kconfig warnings")
  74. # Write the merged configuration and the C header
  75. print(kconf.write_config(args.config_out))
  76. print(kconf.write_autoconf(args.header_out))
  77. # Write the list of parsed Kconfig files to a file
  78. write_kconfig_filenames(kconf, args.kconfig_list_out)
  79. def check_no_promptless_assign(kconf):
  80. # Checks that no promptless symbols are assigned
  81. for sym in kconf.unique_defined_syms:
  82. if sym.user_value is not None and promptless(sym):
  83. err(f"""\
  84. {sym.name_and_loc} is assigned in a configuration file, but is not directly
  85. user-configurable (has no prompt). It gets its value indirectly from other
  86. symbols. """ + SYM_INFO_HINT.format(sym))
  87. def check_assigned_sym_values(kconf):
  88. # Verifies that the values assigned to symbols "took" (matches the value
  89. # the symbols actually got), printing warnings otherwise. Choice symbols
  90. # are checked separately, in check_assigned_choice_values().
  91. for sym in kconf.unique_defined_syms:
  92. if sym.choice:
  93. continue
  94. user_value = sym.user_value
  95. if user_value is None:
  96. continue
  97. # Tristate values are represented as 0, 1, 2. Having them as "n", "m",
  98. # "y" is more convenient here, so convert.
  99. if sym.type in (BOOL, TRISTATE):
  100. user_value = TRI_TO_STR[user_value]
  101. if user_value != sym.str_value:
  102. msg = f"{sym.name_and_loc} was assigned the value '{user_value}'" \
  103. f" but got the value '{sym.str_value}'. "
  104. # List any unsatisfied 'depends on' dependencies in the warning
  105. mdeps = missing_deps(sym)
  106. if mdeps:
  107. expr_strs = []
  108. for expr in mdeps:
  109. estr = expr_str(expr)
  110. if isinstance(expr, tuple):
  111. # Add () around dependencies that aren't plain symbols.
  112. # Gives '(FOO || BAR) (=n)' instead of
  113. # 'FOO || BAR (=n)', which might be clearer.
  114. estr = f"({estr})"
  115. expr_strs.append(f"{estr} "
  116. f"(={TRI_TO_STR[expr_value(expr)]})")
  117. msg += "Check these unsatisfied dependencies: " + \
  118. ", ".join(expr_strs) + ". "
  119. warn(msg + SYM_INFO_HINT.format(sym))
  120. def missing_deps(sym):
  121. # check_assigned_sym_values() helper for finding unsatisfied dependencies.
  122. #
  123. # Given direct dependencies
  124. #
  125. # depends on <expr> && <expr> && ... && <expr>
  126. #
  127. # on 'sym' (which can also come from e.g. a surrounding 'if'), returns a
  128. # list of all <expr>s with a value less than the value 'sym' was assigned
  129. # ("less" instead of "not equal" just to be general and handle tristates,
  130. # even though Zephyr doesn't use them).
  131. #
  132. # For string/int/hex symbols, just looks for <expr> = n.
  133. #
  134. # Note that <expr>s can be something more complicated than just a symbol,
  135. # like 'FOO || BAR' or 'FOO = "string"'.
  136. deps = split_expr(sym.direct_dep, AND)
  137. if sym.type in (BOOL, TRISTATE):
  138. return [dep for dep in deps if expr_value(dep) < sym.user_value]
  139. # string/int/hex
  140. return [dep for dep in deps if expr_value(dep) == 0]
  141. def check_assigned_choice_values(kconf):
  142. # Verifies that any choice symbols that were selected (by setting them to
  143. # y) ended up as the selection, printing warnings otherwise.
  144. #
  145. # We check choice symbols separately to avoid warnings when two different
  146. # choice symbols within the same choice are set to y. This might happen if
  147. # a choice selection from a board defconfig is overridden in a prj.conf,
  148. # for example. The last choice symbol set to y becomes the selection (and
  149. # all other choice symbols get the value n).
  150. #
  151. # Without special-casing choices, we'd detect that the first symbol set to
  152. # y ended up as n, and print a spurious warning.
  153. for choice in kconf.unique_choices:
  154. if choice.user_selection and \
  155. choice.user_selection is not choice.selection:
  156. warn(f"""\
  157. The choice symbol {choice.user_selection.name_and_loc} was selected (set =y),
  158. but {choice.selection.name_and_loc if choice.selection else "no symbol"} ended
  159. up as the choice selection. """ + SYM_INFO_HINT.format(choice.user_selection))
  160. # Hint on where to find symbol information. Used like
  161. # SYM_INFO_HINT.format(sym).
  162. SYM_INFO_HINT = """\
  163. See http://docs.zephyrproject.org/latest/kconfig.html#CONFIG_{0.name} and/or
  164. look up {0.name} in the menuconfig/guiconfig interface. The Application
  165. Development Primer, Setting Configuration Values, and Kconfig - Tips and Best
  166. Practices sections of the manual might be helpful too.\
  167. """
  168. def check_deprecated(kconf):
  169. deprecated = kconf.syms['DEPRECATED']
  170. dep_expr = deprecated.rev_dep
  171. if dep_expr is not kconf.n:
  172. selectors = [s for s in split_expr(dep_expr, OR) if expr_value(s) == 2]
  173. for selector in selectors:
  174. selector_name = split_expr(selector, AND)[0].name
  175. warn(f'Deprecated symbol {selector_name} is enabled.')
  176. def check_experimental(kconf):
  177. experimental = kconf.syms['EXPERIMENTAL']
  178. dep_expr = experimental.rev_dep
  179. if dep_expr is not kconf.n:
  180. selectors = [s for s in split_expr(dep_expr, OR) if expr_value(s) == 2]
  181. for selector in selectors:
  182. selector_name = split_expr(selector, AND)[0].name
  183. warn(f'Experimental symbol {selector_name} is enabled.')
  184. def promptless(sym):
  185. # Returns True if 'sym' has no prompt. Since the symbol might be defined in
  186. # multiple locations, we need to check all locations.
  187. return not any(node.prompt for node in sym.nodes)
  188. def write_kconfig_filenames(kconf, kconfig_list_path):
  189. # Writes a sorted list with the absolute paths of all parsed Kconfig files
  190. # to 'kconfig_list_path'. The paths are realpath()'d, and duplicates are
  191. # removed. This file is used by CMake to look for changed Kconfig files. It
  192. # needs to be deterministic.
  193. with open(kconfig_list_path, 'w') as out:
  194. for path in sorted({os.path.realpath(os.path.join(kconf.srctree, path))
  195. for path in kconf.kconfig_filenames}):
  196. print(path, file=out)
  197. def parse_args():
  198. parser = argparse.ArgumentParser()
  199. parser.add_argument("--handwritten-input-configs",
  200. action="store_true",
  201. help="Assume the input configuration fragments are "
  202. "handwritten fragments and do additional checks "
  203. "on them, like no promptless symbols being "
  204. "assigned")
  205. parser.add_argument("--zephyr-base",
  206. help="Path to current Zephyr installation")
  207. parser.add_argument("kconfig_file",
  208. help="Top-level Kconfig file")
  209. parser.add_argument("config_out",
  210. help="Output configuration file")
  211. parser.add_argument("header_out",
  212. help="Output header file")
  213. parser.add_argument("kconfig_list_out",
  214. help="Output file for list of parsed Kconfig files")
  215. parser.add_argument("configs_in",
  216. nargs="+",
  217. help="Input configuration fragments. Will be merged "
  218. "together.")
  219. return parser.parse_args()
  220. def warn(msg):
  221. # Use a large fill() width to try to avoid linebreaks in the symbol
  222. # reference link, and add some extra newlines to set the message off from
  223. # surrounding text (this usually gets printed as part of spammy CMake
  224. # output)
  225. print("\n" + textwrap.fill("warning: " + msg, 100) + "\n", file=sys.stderr)
  226. def err(msg):
  227. sys.exit("\n" + textwrap.fill("error: " + msg, 100) + "\n")
  228. if __name__ == "__main__":
  229. main()