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