meson-buildoptions.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. #! /usr/bin/env python3
  2. # Generate configure command line options handling code, based on Meson's
  3. # user build options introspection data
  4. #
  5. # Copyright (C) 2021 Red Hat, Inc.
  6. #
  7. # Author: Paolo Bonzini <pbonzini@redhat.com>
  8. #
  9. # This program is free software; you can redistribute it and/or modify
  10. # it under the terms of the GNU General Public License as published by
  11. # the Free Software Foundation; either version 2, or (at your option)
  12. # any later version.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License
  20. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  21. import json
  22. import textwrap
  23. import shlex
  24. import sys
  25. # Options with nonstandard names (e.g. --with/--without) or OS-dependent
  26. # defaults. Try not to add any.
  27. SKIP_OPTIONS = {
  28. "default_devices",
  29. "fuzzing_engine",
  30. }
  31. # Options whose name doesn't match the option for backwards compatibility
  32. # reasons, because Meson gives them a funny name, or both
  33. OPTION_NAMES = {
  34. "b_coverage": "gcov",
  35. "b_lto": "lto",
  36. "coroutine_backend": "with-coroutine",
  37. "debug": "debug-info",
  38. "malloc": "enable-malloc",
  39. "pkgversion": "with-pkgversion",
  40. "qemu_firmwarepath": "firmwarepath",
  41. "qemu_suffix": "with-suffix",
  42. "trace_backends": "enable-trace-backends",
  43. "trace_file": "with-trace-file",
  44. }
  45. # Options that configure autodetects, even though meson defines them as boolean
  46. AUTO_OPTIONS = {
  47. "plugins",
  48. "werror",
  49. }
  50. # Builtin options that should be definable via configure. Some of the others
  51. # we really do not want (e.g. c_args is defined via the native file, not
  52. # via -D, because it's a mix of CFLAGS and --extra-cflags); for specific
  53. # cases "../configure -D" can be used as an escape hatch.
  54. BUILTIN_OPTIONS = {
  55. "b_coverage",
  56. "b_lto",
  57. "bindir",
  58. "datadir",
  59. "debug",
  60. "includedir",
  61. "libdir",
  62. "libexecdir",
  63. "localedir",
  64. "localstatedir",
  65. "mandir",
  66. "prefix",
  67. "strip",
  68. "sysconfdir",
  69. "werror",
  70. }
  71. LINE_WIDTH = 76
  72. # Convert the default value of an option to the string used in
  73. # the help message
  74. def get_help(opt):
  75. if opt["name"] == "libdir":
  76. return 'system default'
  77. value = opt["value"]
  78. if isinstance(value, list):
  79. return ",".join(value)
  80. if isinstance(value, bool):
  81. return "enabled" if value else "disabled"
  82. return str(value)
  83. def wrap(left, text, indent):
  84. spaces = " " * indent
  85. if len(left) >= indent:
  86. yield left
  87. left = spaces
  88. else:
  89. left = (left + spaces)[0:indent]
  90. yield from textwrap.wrap(
  91. text, width=LINE_WIDTH, initial_indent=left, subsequent_indent=spaces
  92. )
  93. def sh_print(line=""):
  94. print(' printf "%s\\n"', shlex.quote(line))
  95. def help_line(left, opt, indent, long):
  96. right = f'{opt["description"]}'
  97. if long:
  98. value = get_help(opt)
  99. if value != "auto" and value != "":
  100. right += f" [{value}]"
  101. if "choices" in opt and long:
  102. choices = "/".join(sorted(opt["choices"]))
  103. right += f" (choices: {choices})"
  104. for x in wrap(" " + left, right, indent):
  105. sh_print(x)
  106. # Return whether the option (a dictionary) can be used with
  107. # arguments. Booleans can never be used with arguments;
  108. # combos allow an argument only if they accept other values
  109. # than "auto", "enabled", and "disabled".
  110. def allow_arg(opt):
  111. if opt["type"] == "boolean":
  112. return False
  113. if opt["type"] != "combo":
  114. return True
  115. return not (set(opt["choices"]) <= {"auto", "disabled", "enabled"})
  116. # Return whether the option (a dictionary) can be used without
  117. # arguments. Booleans can only be used without arguments;
  118. # combos require an argument if they accept neither "enabled"
  119. # nor "disabled"
  120. def require_arg(opt):
  121. if opt["type"] == "boolean":
  122. return False
  123. if opt["type"] != "combo":
  124. return True
  125. return not ({"enabled", "disabled"}.intersection(opt["choices"]))
  126. def filter_options(json):
  127. if ":" in json["name"]:
  128. return False
  129. if json["section"] == "user":
  130. return json["name"] not in SKIP_OPTIONS
  131. else:
  132. return json["name"] in BUILTIN_OPTIONS
  133. def load_options(json):
  134. json = [x for x in json if filter_options(x)]
  135. return sorted(json, key=lambda x: x["name"])
  136. def cli_option(opt):
  137. name = opt["name"]
  138. if name in OPTION_NAMES:
  139. return OPTION_NAMES[name]
  140. return name.replace("_", "-")
  141. def cli_help_key(opt):
  142. key = cli_option(opt)
  143. if require_arg(opt):
  144. return key
  145. if opt["type"] == "boolean" and opt["value"]:
  146. return f"disable-{key}"
  147. return f"enable-{key}"
  148. def cli_metavar(opt):
  149. if opt["type"] == "string":
  150. return "VALUE"
  151. if opt["type"] == "array":
  152. return "CHOICES" if "choices" in opt else "VALUES"
  153. return "CHOICE"
  154. def print_help(options):
  155. print("meson_options_help() {")
  156. feature_opts = []
  157. for opt in sorted(options, key=cli_help_key):
  158. key = cli_help_key(opt)
  159. # The first section includes options that have an arguments,
  160. # and booleans (i.e., only one of enable/disable makes sense)
  161. if require_arg(opt):
  162. metavar = cli_metavar(opt)
  163. left = f"--{key}={metavar}"
  164. help_line(left, opt, 27, True)
  165. elif opt["type"] == "boolean" and opt["name"] not in AUTO_OPTIONS:
  166. left = f"--{key}"
  167. help_line(left, opt, 27, False)
  168. elif allow_arg(opt):
  169. if opt["type"] == "combo" and "enabled" in opt["choices"]:
  170. left = f"--{key}[=CHOICE]"
  171. else:
  172. left = f"--{key}=CHOICE"
  173. help_line(left, opt, 27, True)
  174. else:
  175. feature_opts.append(opt)
  176. sh_print()
  177. sh_print("Optional features, enabled with --enable-FEATURE and")
  178. sh_print("disabled with --disable-FEATURE, default is enabled if available")
  179. sh_print("(unless built with --without-default-features):")
  180. sh_print()
  181. for opt in sorted(feature_opts, key=cli_option):
  182. key = cli_option(opt)
  183. help_line(key, opt, 18, False)
  184. print("}")
  185. def print_parse(options):
  186. print("_meson_option_parse() {")
  187. print(" case $1 in")
  188. for opt in options:
  189. key = cli_option(opt)
  190. name = opt["name"]
  191. if require_arg(opt):
  192. if opt["type"] == "array" and not "choices" in opt:
  193. print(f' --{key}=*) quote_sh "-D{name}=$(meson_option_build_array $2)" ;;')
  194. else:
  195. print(f' --{key}=*) quote_sh "-D{name}=$2" ;;')
  196. elif opt["type"] == "boolean":
  197. print(f' --enable-{key}) printf "%s" -D{name}=true ;;')
  198. print(f' --disable-{key}) printf "%s" -D{name}=false ;;')
  199. else:
  200. if opt["type"] == "combo" and "enabled" in opt["choices"]:
  201. print(f' --enable-{key}) printf "%s" -D{name}=enabled ;;')
  202. if opt["type"] == "combo" and "disabled" in opt["choices"]:
  203. print(f' --disable-{key}) printf "%s" -D{name}=disabled ;;')
  204. if allow_arg(opt):
  205. print(f' --enable-{key}=*) quote_sh "-D{name}=$2" ;;')
  206. print(" *) return 1 ;;")
  207. print(" esac")
  208. print("}")
  209. json_data = sys.stdin.read()
  210. try:
  211. options = load_options(json.loads(json_data))
  212. except:
  213. print("Failure in scripts/meson-buildoptions.py parsing stdin as json",
  214. file=sys.stderr)
  215. print(json_data, file=sys.stderr)
  216. sys.exit(1)
  217. print("# This file is generated by meson-buildoptions.py, do not edit!")
  218. print_help(options)
  219. print_parse(options)