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 "qemu_suffix", 32 "smbd", 33} 34 35OPTION_NAMES = { 36 "b_coverage": "gcov", 37 "b_lto": "lto", 38 "coroutine_backend": "with-coroutine", 39 "debug": "debug-info", 40 "malloc": "enable-malloc", 41 "pkgversion": "with-pkgversion", 42 "qemu_firmwarepath": "firmwarepath", 43 "trace_backends": "enable-trace-backends", 44 "trace_file": "with-trace-file", 45} 46 47BUILTIN_OPTIONS = { 48 "b_coverage", 49 "b_lto", 50 "datadir", 51 "debug", 52 "includedir", 53 "libdir", 54 "libexecdir", 55 "localedir", 56 "localstatedir", 57 "mandir", 58 "strip", 59 "sysconfdir", 60} 61 62LINE_WIDTH = 76 63 64 65# Convert the default value of an option to the string used in 66# the help message 67def get_help(opt): 68 if opt["name"] == "libdir": 69 return 'system default' 70 value = opt["value"] 71 if isinstance(value, list): 72 return ",".join(value) 73 if isinstance(value, bool): 74 return "enabled" if value else "disabled" 75 return str(value) 76 77 78def wrap(left, text, indent): 79 spaces = " " * indent 80 if len(left) >= indent: 81 yield left 82 left = spaces 83 else: 84 left = (left + spaces)[0:indent] 85 yield from textwrap.wrap( 86 text, width=LINE_WIDTH, initial_indent=left, subsequent_indent=spaces 87 ) 88 89 90def sh_print(line=""): 91 print(' printf "%s\\n"', shlex.quote(line)) 92 93 94def help_line(left, opt, indent, long): 95 right = f'{opt["description"]}' 96 if long: 97 value = get_help(opt) 98 if value != "auto" and value != "": 99 right += f" [{value}]" 100 if "choices" in opt and long: 101 choices = "/".join(sorted(opt["choices"])) 102 right += f" (choices: {choices})" 103 for x in wrap(" " + left, right, indent): 104 sh_print(x) 105 106 107# Return whether the option (a dictionary) can be used with 108# arguments. Booleans can never be used with arguments; 109# combos allow an argument only if they accept other values 110# than "auto", "enabled", and "disabled". 111def allow_arg(opt): 112 if opt["type"] == "boolean": 113 return False 114 if opt["type"] != "combo": 115 return True 116 return not (set(opt["choices"]) <= {"auto", "disabled", "enabled"}) 117 118 119# Return whether the option (a dictionary) can be used without 120# arguments. Booleans can only be used without arguments; 121# combos require an argument if they accept neither "enabled" 122# nor "disabled" 123def require_arg(opt): 124 if opt["type"] == "boolean": 125 return False 126 if opt["type"] != "combo": 127 return True 128 return not ({"enabled", "disabled"}.intersection(opt["choices"])) 129 130 131def filter_options(json): 132 if ":" in json["name"]: 133 return False 134 if json["section"] == "user": 135 return json["name"] not in SKIP_OPTIONS 136 else: 137 return json["name"] in BUILTIN_OPTIONS 138 139 140def load_options(json): 141 json = [x for x in json if filter_options(x)] 142 return sorted(json, key=lambda x: x["name"]) 143 144 145def cli_option(opt): 146 name = opt["name"] 147 if name in OPTION_NAMES: 148 return OPTION_NAMES[name] 149 return name.replace("_", "-") 150 151 152def cli_help_key(opt): 153 key = cli_option(opt) 154 if require_arg(opt): 155 return key 156 if opt["type"] == "boolean" and opt["value"]: 157 return f"disable-{key}" 158 return f"enable-{key}" 159 160 161def cli_metavar(opt): 162 if opt["type"] == "string": 163 return "VALUE" 164 if opt["type"] == "array": 165 return "CHOICES" if "choices" in opt else "VALUES" 166 return "CHOICE" 167 168 169def print_help(options): 170 print("meson_options_help() {") 171 for opt in sorted(options, key=cli_help_key): 172 key = cli_help_key(opt) 173 # The first section includes options that have an arguments, 174 # and booleans (i.e., only one of enable/disable makes sense) 175 if require_arg(opt): 176 metavar = cli_metavar(opt) 177 left = f"--{key}={metavar}" 178 help_line(left, opt, 27, True) 179 elif opt["type"] == "boolean": 180 left = f"--{key}" 181 help_line(left, opt, 27, False) 182 elif allow_arg(opt): 183 if opt["type"] == "combo" and "enabled" in opt["choices"]: 184 left = f"--{key}[=CHOICE]" 185 else: 186 left = f"--{key}=CHOICE" 187 help_line(left, opt, 27, True) 188 189 sh_print() 190 sh_print("Optional features, enabled with --enable-FEATURE and") 191 sh_print("disabled with --disable-FEATURE, default is enabled if available") 192 sh_print("(unless built with --without-default-features):") 193 sh_print() 194 for opt in options: 195 key = opt["name"].replace("_", "-") 196 if opt["type"] != "boolean" and not allow_arg(opt): 197 help_line(key, opt, 18, False) 198 print("}") 199 200 201def print_parse(options): 202 print("_meson_option_parse() {") 203 print(" case $1 in") 204 for opt in options: 205 key = cli_option(opt) 206 name = opt["name"] 207 if require_arg(opt): 208 if opt["type"] == "array" and not "choices" in opt: 209 print(f' --{key}=*) quote_sh "-D{name}=$(meson_option_build_array $2)" ;;') 210 else: 211 print(f' --{key}=*) quote_sh "-D{name}=$2" ;;') 212 elif opt["type"] == "boolean": 213 print(f' --enable-{key}) printf "%s" -D{name}=true ;;') 214 print(f' --disable-{key}) printf "%s" -D{name}=false ;;') 215 else: 216 if opt["type"] == "combo" and "enabled" in opt["choices"]: 217 print(f' --enable-{key}) printf "%s" -D{name}=enabled ;;') 218 if opt["type"] == "combo" and "disabled" in opt["choices"]: 219 print(f' --disable-{key}) printf "%s" -D{name}=disabled ;;') 220 if allow_arg(opt): 221 print(f' --enable-{key}=*) quote_sh "-D{name}=$2" ;;') 222 print(" *) return 1 ;;") 223 print(" esac") 224 print("}") 225 226 227options = load_options(json.load(sys.stdin)) 228print("# This file is generated by meson-buildoptions.py, do not edit!") 229print_help(options) 230print_parse(options) 231