Generate option parser in C for kitty CLI
This commit is contained in:
parent
0a288ad56c
commit
27c5b6aac5
7 changed files with 218 additions and 194 deletions
|
|
@ -31,7 +31,6 @@
|
|||
from kitty.cli import (
|
||||
GoOption,
|
||||
go_options_for_seq,
|
||||
parse_option_spec,
|
||||
)
|
||||
from kitty.conf.generate import gen_go_code
|
||||
from kitty.conf.types import Definition
|
||||
|
|
@ -43,7 +42,7 @@
|
|||
from kitty.rc.base import RemoteCommand, all_command_names, command_for_name
|
||||
from kitty.remote_control import global_options_spec
|
||||
from kitty.rgb import color_names
|
||||
from kitty.simple_cli_definitions import CompletionSpec, serialize_as_go_string
|
||||
from kitty.simple_cli_definitions import CompletionSpec, parse_option_spec, serialize_as_go_string
|
||||
|
||||
if __name__ == '__main__' and not __package__:
|
||||
import __main__
|
||||
|
|
|
|||
182
kitty/cli.py
182
kitty/cli.py
|
|
@ -6,14 +6,23 @@
|
|||
import sys
|
||||
from collections.abc import Callable, Iterator, Sequence
|
||||
from re import Match
|
||||
from typing import Any, NoReturn, TypeVar, Union, cast
|
||||
from typing import Any, NoReturn, TypeVar, cast
|
||||
|
||||
from .cli_stub import CLIOptions
|
||||
from .conf.utils import resolve_config
|
||||
from .constants import appname, clear_handled_signals, config_dir, default_pager_for_help, defconf, is_macos, str_version, website_url
|
||||
from .fast_data_types import parse_cli_from_spec, wcswidth
|
||||
from .options.types import Options as KittyOpts
|
||||
from .simple_cli_definitions import CompletionSpec, CompletionType, OptionDict, kitty_options_spec, serialize_as_go_string
|
||||
from .simple_cli_definitions import (
|
||||
CompletionType,
|
||||
OptionDict,
|
||||
OptionSpecSeq,
|
||||
defval_for_opt,
|
||||
get_option_maps,
|
||||
kitty_options_spec,
|
||||
parse_option_spec,
|
||||
serialize_as_go_string,
|
||||
)
|
||||
from .types import run_once
|
||||
from .typing_compat import BadLineType
|
||||
|
||||
|
|
@ -274,103 +283,6 @@ def disc(x: str) -> str:
|
|||
return ref_hyperlink(x, 'discussions-')
|
||||
|
||||
|
||||
OptionSpecSeq = list[Union[str, OptionDict]]
|
||||
|
||||
|
||||
def parse_option_spec(spec: str | None = None) -> tuple[OptionSpecSeq, OptionSpecSeq]:
|
||||
if spec is None:
|
||||
spec = kitty_options_spec()
|
||||
NORMAL, METADATA, HELP = 'NORMAL', 'METADATA', 'HELP'
|
||||
state = NORMAL
|
||||
lines = spec.splitlines()
|
||||
prev_line = ''
|
||||
prev_indent = 0
|
||||
seq: OptionSpecSeq = []
|
||||
disabled: OptionSpecSeq = []
|
||||
mpat = re.compile('([a-z]+)=(.+)')
|
||||
current_cmd: OptionDict = {
|
||||
'dest': '', 'aliases': (), 'help': '', 'choices': (),
|
||||
'type': '', 'condition': False, 'default': None, 'completion': CompletionSpec(), 'name': ''
|
||||
}
|
||||
empty_cmd = current_cmd
|
||||
|
||||
def indent_of_line(x: str) -> int:
|
||||
return len(x) - len(x.lstrip())
|
||||
|
||||
for line in lines:
|
||||
line = line.rstrip()
|
||||
if state is NORMAL:
|
||||
if not line:
|
||||
continue
|
||||
if line.startswith('# '):
|
||||
seq.append(line[2:])
|
||||
continue
|
||||
if line.startswith('--'):
|
||||
parts = line.split(' ')
|
||||
defdest = parts[0][2:].replace('-', '_')
|
||||
current_cmd = {
|
||||
'dest': defdest, 'aliases': tuple(parts), 'help': '',
|
||||
'choices': tuple(), 'type': '', 'name': defdest,
|
||||
'default': None, 'condition': True, 'completion': CompletionSpec(),
|
||||
}
|
||||
state = METADATA
|
||||
continue
|
||||
raise ValueError(f'Invalid option spec, unexpected line: {line}')
|
||||
elif state is METADATA:
|
||||
m = mpat.match(line)
|
||||
if m is None:
|
||||
state = HELP
|
||||
current_cmd['help'] += line
|
||||
else:
|
||||
k, v = m.group(1), m.group(2)
|
||||
if k == 'choices':
|
||||
vals = tuple(x.strip() for x in v.split(','))
|
||||
if not current_cmd['type']:
|
||||
current_cmd['type'] = 'choices'
|
||||
if current_cmd['type'] != 'choices':
|
||||
raise ValueError(f'Cannot specify choices for an option of type: {current_cmd["type"]}')
|
||||
current_cmd['choices'] = tuple(vals)
|
||||
if current_cmd['default'] is None:
|
||||
current_cmd['default'] = vals[0]
|
||||
else:
|
||||
if k == 'default':
|
||||
current_cmd['default'] = v
|
||||
elif k == 'type':
|
||||
if v == 'choice':
|
||||
v = 'choices'
|
||||
current_cmd['type'] = v
|
||||
elif k == 'dest':
|
||||
current_cmd['dest'] = v
|
||||
elif k == 'condition':
|
||||
current_cmd['condition'] = bool(eval(v))
|
||||
elif k == 'completion':
|
||||
current_cmd['completion'] = CompletionSpec.from_string(v)
|
||||
elif state is HELP:
|
||||
if line:
|
||||
current_indent = indent_of_line(line)
|
||||
if current_indent > 1:
|
||||
if prev_indent == 0:
|
||||
current_cmd['help'] += '\n'
|
||||
else:
|
||||
line = line.strip()
|
||||
prev_indent = current_indent
|
||||
spc = '' if current_cmd['help'].endswith('\n') else ' '
|
||||
current_cmd['help'] += spc + line
|
||||
else:
|
||||
prev_indent = 0
|
||||
if prev_line:
|
||||
current_cmd['help'] += '\n' if current_cmd['help'].endswith('::') else '\n\n'
|
||||
else:
|
||||
state = NORMAL
|
||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||
current_cmd = empty_cmd
|
||||
prev_line = line
|
||||
if current_cmd is not empty_cmd:
|
||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||
|
||||
return seq, disabled
|
||||
|
||||
|
||||
def prettify(text: str) -> str:
|
||||
|
||||
def identity(x: str) -> str:
|
||||
|
|
@ -635,21 +547,6 @@ def as_type_stub(seq: OptionSpecSeq, disabled: OptionSpecSeq, class_name: str, e
|
|||
return '\n'.join(ans) + '\n\n\n'
|
||||
|
||||
|
||||
def defval_for_opt(opt: OptionDict) -> Any:
|
||||
dv: Any = opt.get('default')
|
||||
typ = opt.get('type', '')
|
||||
if typ.startswith('bool-'):
|
||||
if dv is None:
|
||||
dv = False if typ == 'bool-set' else True
|
||||
else:
|
||||
dv = dv.lower() in ('true', 'yes', 'y')
|
||||
elif typ == 'list':
|
||||
dv = []
|
||||
elif typ in ('int', 'float'):
|
||||
dv = (int if typ == 'int' else float)(dv or 0)
|
||||
return dv
|
||||
|
||||
|
||||
bool_map = {'y': True, 'yes': True, 'true': True, 'n': False, 'no': False, 'false': False}
|
||||
|
||||
|
||||
|
|
@ -665,19 +562,9 @@ class Options:
|
|||
do_print = True
|
||||
|
||||
def __init__(self, seq: OptionSpecSeq, usage: str | None, message: str | None, appname: str | None):
|
||||
self.alias_map = {}
|
||||
self.seq = seq
|
||||
self.names_map: dict[str, OptionDict] = {}
|
||||
self.values_map: dict[str, Any] = {}
|
||||
self.usage, self.message, self.appname = usage, message, appname
|
||||
for opt in seq:
|
||||
if isinstance(opt, str):
|
||||
continue
|
||||
for alias in opt['aliases']:
|
||||
self.alias_map[alias] = opt
|
||||
name = opt['dest']
|
||||
self.names_map[name] = opt
|
||||
self.values_map[name] = defval_for_opt(opt)
|
||||
self.names_map, self.alias_map, self.values_map = get_option_maps(seq)
|
||||
|
||||
def handle_help(self) -> NoReturn:
|
||||
if self.do_print:
|
||||
|
|
@ -689,45 +576,6 @@ def handle_version(self) -> NoReturn:
|
|||
print(version())
|
||||
raise SystemExit(0)
|
||||
|
||||
def is_bool(self, alias: str) -> bool:
|
||||
opt = self.alias_map[alias]
|
||||
typ = opt.get('type', '')
|
||||
return typ.startswith('bool-')
|
||||
|
||||
def process_arg(self, alias: str, val: Any = None) -> None:
|
||||
opt = self.alias_map[alias]
|
||||
typ = opt.get('type', '')
|
||||
name = opt['dest']
|
||||
nmap = {'float': float, 'int': int}
|
||||
if typ == 'bool-set':
|
||||
if val is None:
|
||||
self.values_map[name] = True
|
||||
else:
|
||||
self.values_map[name] = to_bool(alias, val)
|
||||
elif typ == 'bool-reset':
|
||||
if val is None:
|
||||
self.values_map[name] = False
|
||||
else:
|
||||
self.values_map[name] = to_bool(alias, val)
|
||||
elif typ == 'list':
|
||||
self.values_map.setdefault(name, [])
|
||||
self.values_map[name].append(val)
|
||||
elif typ == 'choices':
|
||||
choices = opt['choices']
|
||||
if val not in choices:
|
||||
raise SystemExit('{} is not a valid value for the {} option. Valid values are: {}'.format(
|
||||
val, emph(alias), ', '.join(choices)))
|
||||
self.values_map[name] = val
|
||||
elif typ in nmap:
|
||||
f = nmap[typ]
|
||||
try:
|
||||
self.values_map[name] = f(val)
|
||||
except Exception:
|
||||
raise SystemExit('{} is not a valid value for the {} option, a number is required.'.format(
|
||||
val, emph(alias)))
|
||||
else:
|
||||
self.values_map[name] = val
|
||||
|
||||
|
||||
def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: list[str] | None = None) -> list[str]:
|
||||
try:
|
||||
|
|
@ -736,10 +584,10 @@ def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: list[str
|
|||
raise SystemExit(str(e))
|
||||
|
||||
for key, (val, is_seen) in vals.items():
|
||||
if key == 'version' and is_seen and val:
|
||||
oc.handle_version()
|
||||
elif key == 'help' and is_seen and val:
|
||||
if key == 'help' and is_seen and val:
|
||||
oc.handle_help()
|
||||
elif key == 'version' and is_seen and val:
|
||||
oc.handle_version()
|
||||
setattr(ans, key, val)
|
||||
for opt in disabled:
|
||||
if not isinstance(opt, str):
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ exec_kitten(int argc, char *argv[], char *exe_dir) {
|
|||
|
||||
static bool
|
||||
is_boolean_flag(const char *x) {
|
||||
static const char *all_boolean_options = KITTY_CLI_BOOL_OPTIONS;
|
||||
static const char *all_boolean_options = "";
|
||||
char buf[128];
|
||||
safe_snprintf(buf, sizeof(buf), " %s ", x);
|
||||
return strstr(all_boolean_options, buf) != NULL;
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
from io import BytesIO
|
||||
from typing import TYPE_CHECKING, Any, NoReturn, Optional, Union, cast
|
||||
|
||||
from kitty.cli import get_defaults_from_seq, parse_args, parse_option_spec
|
||||
from kitty.cli import get_defaults_from_seq, parse_args
|
||||
from kitty.cli_stub import RCOptions as R
|
||||
from kitty.constants import list_kitty_resources, running_in_kitty
|
||||
from kitty.simple_cli_definitions import CompletionSpec
|
||||
from kitty.simple_cli_definitions import CompletionSpec, parse_option_spec
|
||||
from kitty.types import AsyncResponse
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
|
|
@ -4,20 +4,16 @@
|
|||
# This module must be runnable by a vanilla python interperter
|
||||
# as it is used to generate C code when building kitty
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, auto
|
||||
from typing import Iterator, TypedDict
|
||||
from typing import Any, Iterator, TypedDict
|
||||
|
||||
try:
|
||||
from kitty.constants import appname, is_macos
|
||||
except ImportError:
|
||||
is_macos = 'darwin' in sys.platform.lower()
|
||||
try:
|
||||
appname
|
||||
except NameError:
|
||||
appname = os.environ['KITTY_APPNAME']
|
||||
try:
|
||||
from kitty.utils import shlex_split as ksplit
|
||||
def shlex_split(text: str) -> Iterator[str]:
|
||||
|
|
@ -122,6 +118,182 @@ class OptionDict(TypedDict):
|
|||
completion: CompletionSpec
|
||||
|
||||
|
||||
OptionSpecSeq = list[str | OptionDict]
|
||||
|
||||
|
||||
def parse_option_spec(spec: str | None = None) -> tuple[OptionSpecSeq, OptionSpecSeq]:
|
||||
if spec is None:
|
||||
spec = kitty_options_spec()
|
||||
NORMAL, METADATA, HELP = 'NORMAL', 'METADATA', 'HELP'
|
||||
state = NORMAL
|
||||
lines = spec.splitlines()
|
||||
prev_line = ''
|
||||
prev_indent = 0
|
||||
seq: OptionSpecSeq = []
|
||||
disabled: OptionSpecSeq = []
|
||||
mpat = re.compile('([a-z]+)=(.+)')
|
||||
current_cmd: OptionDict = {
|
||||
'dest': '', 'aliases': (), 'help': '', 'choices': (),
|
||||
'type': '', 'condition': False, 'default': None, 'completion': CompletionSpec(), 'name': ''
|
||||
}
|
||||
empty_cmd = current_cmd
|
||||
|
||||
def indent_of_line(x: str) -> int:
|
||||
return len(x) - len(x.lstrip())
|
||||
|
||||
for line in lines:
|
||||
line = line.rstrip()
|
||||
if state is NORMAL:
|
||||
if not line:
|
||||
continue
|
||||
if line.startswith('# '):
|
||||
seq.append(line[2:])
|
||||
continue
|
||||
if line.startswith('--'):
|
||||
parts = line.split(' ')
|
||||
defdest = parts[0][2:].replace('-', '_')
|
||||
current_cmd = {
|
||||
'dest': defdest, 'aliases': tuple(parts), 'help': '',
|
||||
'choices': tuple(), 'type': '', 'name': defdest,
|
||||
'default': None, 'condition': True, 'completion': CompletionSpec(),
|
||||
}
|
||||
state = METADATA
|
||||
continue
|
||||
raise ValueError(f'Invalid option spec, unexpected line: {line}')
|
||||
elif state is METADATA:
|
||||
m = mpat.match(line)
|
||||
if m is None:
|
||||
state = HELP
|
||||
current_cmd['help'] += line
|
||||
else:
|
||||
k, v = m.group(1), m.group(2)
|
||||
if k == 'choices':
|
||||
vals = tuple(x.strip() for x in v.split(','))
|
||||
if not current_cmd['type']:
|
||||
current_cmd['type'] = 'choices'
|
||||
if current_cmd['type'] != 'choices':
|
||||
raise ValueError(f'Cannot specify choices for an option of type: {current_cmd["type"]}')
|
||||
current_cmd['choices'] = tuple(vals)
|
||||
if current_cmd['default'] is None:
|
||||
current_cmd['default'] = vals[0]
|
||||
else:
|
||||
if k == 'default':
|
||||
current_cmd['default'] = v
|
||||
elif k == 'type':
|
||||
if v == 'choice':
|
||||
v = 'choices'
|
||||
current_cmd['type'] = v
|
||||
elif k == 'dest':
|
||||
current_cmd['dest'] = v
|
||||
elif k == 'condition':
|
||||
current_cmd['condition'] = bool(eval(v))
|
||||
elif k == 'completion':
|
||||
current_cmd['completion'] = CompletionSpec.from_string(v)
|
||||
elif state is HELP:
|
||||
if line:
|
||||
current_indent = indent_of_line(line)
|
||||
if current_indent > 1:
|
||||
if prev_indent == 0:
|
||||
current_cmd['help'] += '\n'
|
||||
else:
|
||||
line = line.strip()
|
||||
prev_indent = current_indent
|
||||
spc = '' if current_cmd['help'].endswith('\n') else ' '
|
||||
current_cmd['help'] += spc + line
|
||||
else:
|
||||
prev_indent = 0
|
||||
if prev_line:
|
||||
current_cmd['help'] += '\n' if current_cmd['help'].endswith('::') else '\n\n'
|
||||
else:
|
||||
state = NORMAL
|
||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||
current_cmd = empty_cmd
|
||||
prev_line = line
|
||||
if current_cmd is not empty_cmd:
|
||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||
|
||||
return seq, disabled
|
||||
|
||||
|
||||
def defval_for_opt(opt: OptionDict) -> Any:
|
||||
dv: Any = opt.get('default')
|
||||
typ = opt.get('type', '')
|
||||
if typ.startswith('bool-'):
|
||||
if dv is None:
|
||||
dv = False if typ == 'bool-set' else True
|
||||
else:
|
||||
dv = dv.lower() in ('true', 'yes', 'y')
|
||||
elif typ == 'list':
|
||||
dv = []
|
||||
elif typ in ('int', 'float'):
|
||||
dv = (int if typ == 'int' else float)(dv or 0)
|
||||
return dv
|
||||
|
||||
|
||||
def get_option_maps(seq: OptionSpecSeq) -> tuple[dict[str, OptionDict], dict[str, OptionDict], dict[str, Any]]:
|
||||
names_map: dict[str, OptionDict] = {}
|
||||
alias_map: dict[str, OptionDict] = {}
|
||||
values_map: dict[str, Any] = {}
|
||||
for opt in seq:
|
||||
if isinstance(opt, str):
|
||||
continue
|
||||
for alias in opt['aliases']:
|
||||
alias_map[alias] = opt
|
||||
name = opt['dest']
|
||||
names_map[name] = opt
|
||||
values_map[name] = defval_for_opt(opt)
|
||||
return names_map, alias_map, values_map
|
||||
|
||||
|
||||
def c_str(x: str) -> str:
|
||||
x = x.replace('\\', r'\\')
|
||||
return f'"{x}"'
|
||||
|
||||
|
||||
def generate_c_parser_for(funcname: str, spec: str) -> Iterator[str]:
|
||||
names_map, _, defaults_map = get_option_maps(parse_option_spec(spec)[0])
|
||||
yield f'static void\nparse_cli_for_{funcname}(CLISpec *spec, int argc, char **argv) {{' # }}
|
||||
yield '\tFlagSpec flag;'
|
||||
for name, opt in names_map.items():
|
||||
for alias in opt['aliases']:
|
||||
yield f'\tif (vt_is_end(vt_insert(&spec->alias_map, {c_str(alias)}, {c_str(name)}))) OOM;'
|
||||
yield f'\tflag = (FlagSpec){{.dest={c_str(name)},}};'
|
||||
defval = defaults_map[name]
|
||||
match opt['type']:
|
||||
case 'bool-set' | 'bool-reset':
|
||||
yield '\tflag.defval.type = CLI_VALUE_BOOL;'
|
||||
yield f'\tflag.defval.boolval = {"true" if defval else "false"};'
|
||||
case 'int':
|
||||
yield '\tflag.defval.type = CLI_VALUE_INT;'
|
||||
yield f'\tflag.defval.intval = {defval};'
|
||||
case 'float':
|
||||
yield '\tflag.defval.type = CLI_VALUE_FLOAT;'
|
||||
yield f'\tflag.defval.floatval = {defval};'
|
||||
case 'list':
|
||||
yield '\tflag.defval.type = CLI_VALUE_LIST;'
|
||||
case 'choices':
|
||||
yield '\tflag.defval.type = CLI_VALUE_CHOICE;'
|
||||
yield f'\tflag.defval.strval = {c_str(defval)};'
|
||||
choices = opt['choices']
|
||||
yield f'\tflag.defval.listval.items = alloc_for_cli(spec, {len(choices)} * sizeof(flag.defval.listval.items[0]));'
|
||||
yield '\tif (!flag.defval.listval.items) OOM;'
|
||||
yield f'\tflag.defval.listval.count = {len(choices)};'
|
||||
yield f'\tflag.defval.listval.capacity = {len(choices)};'
|
||||
for n, choice in enumerate(choices):
|
||||
yield f'\tflag.defval.listval.items[{n}] = {c_str(choice)};'
|
||||
case _:
|
||||
yield '\tflag.defval.type = CLI_VALUE_STRING;'
|
||||
yield f'\tflag.defval.strval = {"NULL" if defval is None else c_str(defval)};'
|
||||
yield '\tif (vt_is_end(vt_insert(&spec->flag_map, flag.dest, flag))) OOM;'
|
||||
yield '}'
|
||||
|
||||
|
||||
def generate_c_parsers() -> Iterator[str]:
|
||||
yield '#pragma once'
|
||||
yield '// generated by simple_cli_definitions.py do NOT edit!'
|
||||
yield '#include "cli-parser.h"'
|
||||
yield from generate_c_parser_for('kitty', kitty_options_spec())
|
||||
yield ''
|
||||
|
||||
|
||||
listen_on_defn = f'''\
|
||||
|
|
@ -336,7 +508,3 @@ def kitty_options_spec() -> str:
|
|||
))
|
||||
ans: str = getattr(kitty_options_spec, 'ans')
|
||||
return ans
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(111111111, appname)
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ def t(args, leftover=(), fails=False, **expected):
|
|||
t('-1 -0v', fails=True)
|
||||
t('-1 -v0', fails=True)
|
||||
t('-1 --version', fails=True)
|
||||
t('-f=3.142 --int 17', float=3.142, int=17)
|
||||
|
||||
|
||||
def conf_parsing(self):
|
||||
|
|
|
|||
32
setup.py
32
setup.py
|
|
@ -1058,17 +1058,30 @@ def extract_rst_targets() -> Dict[str, Dict[str, str]]:
|
|||
return cast(Dict[str, Dict[str, str]], m['main']())
|
||||
|
||||
|
||||
def update_if_changed(path: str, text: str) -> None:
|
||||
q = ''
|
||||
with suppress(FileNotFoundError), open(path) as f:
|
||||
q = f.read()
|
||||
if q != text:
|
||||
with open(path, 'w') as f:
|
||||
f.write(text)
|
||||
|
||||
|
||||
def build_ref_map(skip_generation: bool = False) -> str:
|
||||
dest = 'kitty/docs_ref_map_generated.h'
|
||||
if not skip_generation:
|
||||
d = extract_rst_targets()
|
||||
h = 'static const char docs_ref_map[] = {\n' + textwrap.fill(', '.join(map(str, bytearray(json.dumps(d, sort_keys=True).encode('utf-8'))))) + '\n};\n'
|
||||
q = ''
|
||||
with suppress(FileNotFoundError), open(dest) as f:
|
||||
q = f.read()
|
||||
if q != h:
|
||||
with open(dest, 'w') as f:
|
||||
f.write(h)
|
||||
update_if_changed(dest, h)
|
||||
return dest
|
||||
|
||||
|
||||
def build_cli_parser_specs(skip_generation: bool = False) -> str:
|
||||
dest = 'kitty/launcher/cli-parser-data_generated.h'
|
||||
if not skip_generation:
|
||||
m = runpy.run_path('kitty/simple_cli_definitions.py', {'appname': appname})
|
||||
h = '\n'.join(m['generate_c_parsers']())
|
||||
update_if_changed(dest, h)
|
||||
return dest
|
||||
|
||||
|
||||
|
|
@ -1136,6 +1149,7 @@ def build(args: Options, native_optimizations: bool = True, call_init: bool = Tr
|
|||
init_env_from_args(args, native_optimizations)
|
||||
sources, headers = find_c_files()
|
||||
headers.append(build_ref_map(args.skip_code_generation))
|
||||
headers.append(build_cli_parser_specs(args.skip_code_generation))
|
||||
headers.append(build_uniforms_header(args.skip_code_generation))
|
||||
compile_c_extension(
|
||||
kitty_env(args), 'kitty/fast_data_types', args.compilation_database, sources, headers,
|
||||
|
|
@ -1281,11 +1295,6 @@ def read_bool_options(path: str = 'kitty/cli.py') -> Tuple[str, ...]:
|
|||
return tuple(ans)
|
||||
|
||||
|
||||
@lru_cache(2)
|
||||
def kitty_cli_boolean_options() -> Tuple[str, ...]:
|
||||
return tuple(sorted(set(read_bool_options()) | set(read_bool_options('kittens/panel/main.py'))))
|
||||
|
||||
|
||||
def build_launcher(args: Options, launcher_dir: str = '.', bundle_type: str = 'source') -> str:
|
||||
werror = '' if args.ignore_compiler_warnings else '-pedantic-errors -Werror'
|
||||
cflags = f'-Wall {werror} -fpie'.split()
|
||||
|
|
@ -1343,7 +1352,6 @@ def build_launcher(args: Options, launcher_dir: str = '.', bundle_type: str = 's
|
|||
os.makedirs(launcher_dir, exist_ok=True)
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
objects = []
|
||||
cppflags.append('-DKITTY_CLI_BOOL_OPTIONS=" ' + ' '.join(kitty_cli_boolean_options()) + ' "')
|
||||
cppflags.append('-DKITTY_VERSION="' + '.'.join(map(str, version)) + '"')
|
||||
for src in ('kitty/launcher/main.c', 'kitty/launcher/single-instance.c', 'kitty/launcher/cmdline.c'):
|
||||
obj = os.path.join(build_dir, src.replace('/', '-').replace('.c', '.o'))
|
||||
|
|
|
|||
Loading…
Reference in a new issue