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 23import json 24import textwrap 25import shlex 26import sys 27 28SKIP_OPTIONS = { 29 "default_devices", 30 "fuzzing_engine", 31} 32 33OPTION_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 47AUTO_OPTIONS = { 48 "plugins", 49 "werror", 50} 51 52BUILTIN_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 70LINE_WIDTH = 76 71 72 73# Convert the default value of an option to the string used in 74# the help message 75def 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 86def 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 98def sh_print(line=""): 99 print(' printf "%s\\n"', shlex.quote(line)) 100 101 102def 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". 119def 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" 131def 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 139def 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 148def 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 153def cli_option(opt): 154 name = opt["name"] 155 if name in OPTION_NAMES: 156 return OPTION_NAMES[name] 157 return name.replace("_", "-") 158 159 160def 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 169def 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 177def 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 211def 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 237options = load_options(json.load(sys.stdin)) 238print("# This file is generated by meson-buildoptions.py, do not edit!") 239print_help(options) 240print_parse(options) 241