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