Skip to content

Commit

Permalink
using a typing_extensions.Protocol for command line arguments
Browse files Browse the repository at this point in the history
This allows static analysis tools to analyze.
  • Loading branch information
dcbaker committed Dec 11, 2023
1 parent b1d2282 commit ac73347
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 21 deletions.
51 changes: 44 additions & 7 deletions mesonbuild/coredata.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,50 @@
import typing as T

if T.TYPE_CHECKING:
from typing_extensions import Protocol

from . import dependencies
from .compilers.compilers import Compiler, CompileResult, RunResult, CompileCheckMode
from .dependencies.detect import TV_DepID
from .environment import Environment
from .mesonlib import FileOrString
from .cmake.traceparser import CMakeCacheEntry

class CommandLineOptions(Protocol):

"""Representation of command line options from Meson setup and Meson
configure.
:param builddir: The path to the build directory
:param clearcache: whether to wipe caches
:param cmd_line_options: Raw options converted into `OptionKey: str`
pairs
:param cross_file: List of cross files passed
:param fatal_warnings: whether Meson warnings should cause configuration
to stop
:param native_file: List of native files pass
:param pager: Should Meson start a pager for it's configure output?
:param profile: whether to profile the meson configuration step
:param project_options: A list of row option passed on the command line
:param reconfigure: should Meson should force a fall reconfigure even if
it's otherwise unnecessary
:param sourcedir: The path to the source directory
:param wipe: whether to wipe the build dir
"""

builddir: str
clearcache: bool
cmd_line_options: T.Dict[OptionKey, str]
cross_file: T.List[str]
fatal_warnings: bool
native_file: T.List[str]
pager: bool
profile: bool
projectoptions: T.List[str]
reconfigure: bool
sourcedir: str
wipe: bool

OptionDictType = T.Union[T.Dict[str, 'UserOption[T.Any]'], 'OptionsView']
MutableKeyedOptionDictType = T.Dict['OptionKey', 'UserOption[T.Any]']
KeyedOptionDictType = T.Union[MutableKeyedOptionDictType, 'OptionsView']
Expand Down Expand Up @@ -555,7 +592,7 @@ def languages(self) -> T.Set[str]:

class CoreData:

def __init__(self, options: argparse.Namespace, scratch_dir: str, meson_command: T.List[str]):
def __init__(self, options: CommandLineOptions, scratch_dir: str, meson_command: T.List[str]):
self.lang_guids = {
'default': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
'c': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
Expand Down Expand Up @@ -596,7 +633,7 @@ def __init__(self, options: argparse.Namespace, scratch_dir: str, meson_command:
self.init_builtins('')

@staticmethod
def __load_config_files(options: argparse.Namespace, scratch_dir: str, ftype: str) -> T.List[str]:
def __load_config_files(options: CommandLineOptions, scratch_dir: str, ftype: str) -> T.List[str]:
# Need to try and make the passed filenames absolute because when the
# files are parsed later we'll have chdir()d.
if ftype == 'cross':
Expand Down Expand Up @@ -1128,7 +1165,7 @@ def parse_machine_files(filenames: T.List[str], sourcedir: str):
def get_cmd_line_file(build_dir: str) -> str:
return os.path.join(build_dir, 'meson-private', 'cmd_line.txt')

def read_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
def read_cmd_line_file(build_dir: str, options: CommandLineOptions) -> None:
filename = get_cmd_line_file(build_dir)
if not os.path.isfile(filename):
return
Expand All @@ -1150,7 +1187,7 @@ def read_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
# literal_eval to get it into the list of strings.
options.native_file = ast.literal_eval(properties.get('native_file', '[]'))

def write_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
def write_cmd_line_file(build_dir: str, options: CommandLineOptions) -> None:
filename = get_cmd_line_file(build_dir)
config = CmdLineFileParser()

Expand All @@ -1165,15 +1202,15 @@ def write_cmd_line_file(build_dir: str, options: argparse.Namespace) -> None:
with open(filename, 'w', encoding='utf-8') as f:
config.write(f)

def update_cmd_line_file(build_dir: str, options: argparse.Namespace):
def update_cmd_line_file(build_dir: str, options: CommandLineOptions):
filename = get_cmd_line_file(build_dir)
config = CmdLineFileParser()
config.read(filename)
config['options'].update({str(k): str(v) for k, v in options.cmd_line_options.items()})
with open(filename, 'w', encoding='utf-8') as f:
config.write(f)

def format_cmd_line_options(options: argparse.Namespace) -> str:
def format_cmd_line_options(options: CommandLineOptions) -> str:
cmdline = ['-D{}={}'.format(str(k), v) for k, v in options.cmd_line_options.items()]
if options.cross_file:
cmdline += [f'--cross-file={f}' for f in options.cross_file]
Expand Down Expand Up @@ -1231,7 +1268,7 @@ def create_options_dict(options: T.List[str], subproject: str = '') -> T.Dict[Op
result[k] = value
return result

def parse_cmd_line_options(args: argparse.Namespace) -> None:
def parse_cmd_line_options(args: CommandLineOptions) -> None:
args.cmd_line_options = create_options_dict(args.projectoptions)

# Merge builtin options set with --option into the dict.
Expand Down
5 changes: 2 additions & 3 deletions mesonbuild/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
from mesonbuild import envconfig

if T.TYPE_CHECKING:
import argparse
from configparser import ConfigParser

from .compilers import Compiler
Expand Down Expand Up @@ -495,7 +494,7 @@ class Environment:
log_dir = 'meson-logs'
info_dir = 'meson-info'

def __init__(self, source_dir: str, build_dir: str, options: 'argparse.Namespace') -> None:
def __init__(self, source_dir: str, build_dir: str, options: coredata.CommandLineOptions) -> None:
self.source_dir = source_dir
self.build_dir = build_dir
# Do not try to create build directories when build_dir is none.
Expand Down Expand Up @@ -790,7 +789,7 @@ def _set_default_properties_from_env(self) -> None:
self.properties[for_machine].properties.setdefault(name, p_env)
break

def create_new_coredata(self, options: 'argparse.Namespace') -> None:
def create_new_coredata(self, options: coredata.CommandLineOptions) -> None:
# WARNING: Don't use any values from coredata in __init__. It gets
# re-initialized with project options by the interpreter during
# build file parsing.
Expand Down
4 changes: 1 addition & 3 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@
import copy

if T.TYPE_CHECKING:
import argparse

from . import kwargs as kwtypes
from ..backend.backends import Backend
from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs
Expand Down Expand Up @@ -278,7 +276,7 @@ def __init__(
ast: T.Optional[mparser.CodeBlockNode] = None,
is_translated: bool = False,
relaxations: T.Optional[T.Set[InterpreterRuleRelaxation]] = None,
user_defined_options: T.Optional['argparse.Namespace'] = None,
user_defined_options: T.Optional[coredata.CommandLineOptions] = None,
) -> None:
super().__init__(_build.environment.get_source_dir(), subdir, subproject)
self.active_projectname = ''
Expand Down
4 changes: 2 additions & 2 deletions mesonbuild/mconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def print_nondefault_buildtype_options(self) -> None:
for m in mismatching:
mlog.log(f'{m[0]:21}{m[1]:10}{m[2]:10}')

def run_impl(options: argparse.Namespace, builddir: str) -> int:
def run_impl(options: coredata.CommandLineOptions, builddir: str) -> int:
print_only = not options.cmd_line_options and not options.clearcache
c = None
try:
Expand Down Expand Up @@ -335,7 +335,7 @@ def run_impl(options: argparse.Namespace, builddir: str) -> int:
pass
return 0

def run(options: argparse.Namespace) -> int:
def run(options: coredata.CommandLineOptions) -> int:
coredata.parse_cmd_line_options(options)
builddir = os.path.abspath(os.path.realpath(options.builddir))
return run_impl(options, builddir)
15 changes: 9 additions & 6 deletions mesonbuild/msetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from . import build, coredata, environment, interpreter, mesonlib, mintro, mlog
from .mesonlib import MesonException

if T.TYPE_CHECKING:
from .coredata import CommandLineOptions

git_ignore_file = '''# This file is autogenerated by Meson. If you change or delete it, it won't be recreated.
*
'''
Expand Down Expand Up @@ -63,7 +66,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
parser.add_argument('sourcedir', nargs='?', default=None)

class MesonApp:
def __init__(self, options: argparse.Namespace) -> None:
def __init__(self, options: CommandLineOptions) -> None:
self.options = options
(self.source_dir, self.build_dir) = self.validate_dirs()
if options.wipe:
Expand Down Expand Up @@ -183,7 +186,7 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) -
def _generate(self, env: environment.Environment, capture: bool, vslite_ctx: T.Optional[dict]) -> T.Optional[dict]:
# Get all user defined options, including options that have been defined
# during a previous invocation or using meson configure.
user_defined_options = argparse.Namespace(**vars(self.options))
user_defined_options = T.cast('CommandLineOptions', argparse.Namespace(**vars(self.options)))
coredata.read_cmd_line_file(self.build_dir, user_defined_options)

mlog.debug('Build started at', datetime.datetime.now().isoformat())
Expand Down Expand Up @@ -310,7 +313,7 @@ def finalize_postconf_hooks(self, b: build.Build, intr: interpreter.Interpreter)
for mod in intr.modules.values():
mod.postconf_hook(b)

def run_genvslite_setup(options: argparse.Namespace) -> None:
def run_genvslite_setup(options: CommandLineOptions) -> None:
# With --genvslite, we essentially want to invoke multiple 'setup' iterations. I.e. -
# meson setup ... builddirprefix_debug
# meson setup ... builddirprefix_debugoptimized
Expand Down Expand Up @@ -344,11 +347,11 @@ def run_genvslite_setup(options: argparse.Namespace) -> None:
app = MesonApp(options)
app.generate(capture=False, vslite_ctx=vslite_ctx)

def run(options: T.Union[argparse.Namespace, T.List[str]]) -> int:
if not isinstance(options, argparse.Namespace):
def run(options: T.Union[CommandLineOptions, T.List[str]]) -> int:
if isinstance(options, list):
parser = argparse.ArgumentParser()
add_arguments(parser)
options = parser.parse_args(options)
options = T.cast('CommandLineOptions', parser.parse_args(options))
coredata.parse_cmd_line_options(options)

if mesonlib.OptionKey('genvslite') in options.cmd_line_options.keys():
Expand Down

0 comments on commit ac73347

Please sign in to comment.