From d72df42512cbb6c4a182d0e7557f2f661def14b5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 4 Oct 2025 12:45:49 +0200 Subject: [PATCH 1/8] qt: remove stale comment There is no generator list in the Interpreter. Signed-off-by: Paolo Bonzini --- mesonbuild/modules/_qt.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 675c174a7b92..a02dd3b7a420 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -513,7 +513,6 @@ def _compile_ui_impl(self, state: ModuleState, kwargs: UICompilerKwArgs) -> buil raise MesonException(err_msg.format('UIC', f'uic-qt{self.qt_version}', self.qt_version)) preserve_path_from = os.path.join(state.source_root, state.subdir) if kwargs['preserve_paths'] else None - # TODO: This generator isn't added to the generator list in the Interpreter gen = build.Generator( self.tools['uic'], kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'], From b9dfb530465c6ca16b88046dee27c0b91da9c0ce Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 4 Oct 2025 15:30:50 +0200 Subject: [PATCH 2/8] build: import Environment --- mesonbuild/build.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 7c83a2f57687..aa656c478da5 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -38,7 +38,7 @@ if T.TYPE_CHECKING: from typing_extensions import Literal, TypeAlias, TypedDict - from . import environment + from .environment import Environment from ._typing import ImmutableListProtocol from .backend.backends import Backend from .compilers import Compiler @@ -332,7 +332,7 @@ class Build: all dependencies and so on. """ - def __init__(self, environment: environment.Environment): + def __init__(self, environment: Environment): self.version = coredata.version self.project_name = 'name of master project' self.project_version: T.Optional[str] = None @@ -605,7 +605,7 @@ class Target(HoldableObject, metaclass=abc.ABCMeta): subproject: 'SubProject' build_by_default: bool for_machine: MachineChoice - environment: environment.Environment + environment: Environment install: bool = False build_always_stale: bool = False extra_files: T.List[File] = field(default_factory=list) @@ -761,7 +761,7 @@ def __init__( sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.List[ObjectTypes], - environment: environment.Environment, + environment: Environment, compilers: T.Dict[str, 'Compiler'], kwargs: BuildTargetKeywordArguments): super().__init__(name, subdir, subproject, True, for_machine, environment, install=kwargs.get('install', False)) @@ -2152,7 +2152,7 @@ def __init__( sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.List[ObjectTypes], - environment: environment.Environment, + environment: Environment, compilers: T.Dict[str, 'Compiler'], kwargs: ExecutableKeywordArguments): super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, @@ -2305,7 +2305,7 @@ def __init__( sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.List[ObjectTypes], - environment: environment.Environment, + environment: Environment, compilers: T.Dict[str, 'Compiler'], kwargs: StaticLibraryKeywordArguments): self.prelink = kwargs.get('prelink', False) @@ -2450,7 +2450,7 @@ def __init__( sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.List[ObjectTypes], - environment: environment.Environment, + environment: Environment, compilers: T.Dict[str, 'Compiler'], kwargs): self.soversion: T.Optional[str] = None @@ -2744,7 +2744,7 @@ def __init__( sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], objects: T.List[ObjectTypes], - environment: environment.Environment, + environment: Environment, compilers: T.Dict[str, 'Compiler'], kwargs): if 'version' in kwargs: @@ -2860,7 +2860,7 @@ def __init__(self, name: T.Optional[str], subdir: str, subproject: str, - environment: environment.Environment, + environment: Environment, command: T.Sequence[T.Union[ str, BuildTargetTypes, GeneratedList, programs.ExternalProgram, File]], @@ -3071,7 +3071,7 @@ def __init__(self, name: str, subdir: str, subproject: str, - environment: environment.Environment, + environment: Environment, sources: T.List['SourceOutputs'], output_templ: str, compiler: Compiler, @@ -3127,7 +3127,7 @@ def __init__(self, name: str, dependencies: T.Sequence[AnyTargetType], subdir: str, subproject: str, - environment: environment.Environment, + environment: Environment, env: T.Optional[EnvironmentVariables] = None, default_env: bool = True): # These don't produce output artifacts @@ -3174,7 +3174,7 @@ class AliasTarget(RunTarget): typename = 'alias' def __init__(self, name: str, dependencies: T.Sequence[Target], - subdir: str, subproject: str, environment: environment.Environment): + subdir: str, subproject: str, environment: Environment): super().__init__(name, [], dependencies, subdir, subproject, environment) def __repr__(self): @@ -3188,7 +3188,7 @@ class Jar(BuildTarget): def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, sources: T.List[SourceOutputs], structured_sources: T.Optional['StructuredSources'], - objects, environment: environment.Environment, compilers: T.Dict[str, 'Compiler'], + objects, environment: Environment, compilers: T.Dict[str, 'Compiler'], kwargs): super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, compilers, kwargs) From b952921b40d1b57f0af62253e4ccdff6fe221287 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 4 Oct 2025 12:47:17 +0200 Subject: [PATCH 3/8] build: pass Environment to GeneratedList GeneratedList does not need the full interpreter, only the source and build directories which can be found in Environment. Signed-off-by: Paolo Bonzini --- mesonbuild/build.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index aa656c478da5..1cba2f6b466c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2062,7 +2062,7 @@ def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], if not is_parent_path(preserve_path_from, abs_f): raise InvalidArguments('generator.process: When using preserve_path_from, all input files must be in a subdirectory of the given dir.') f = FileMaybeInTargetPrivateDir(f) - output.add_file(f, state) + output.add_file(f, state.environment) return output @@ -2101,9 +2101,9 @@ def __post_init__(self) -> None: # know the absolute path of self.depend_files.append(File.from_absolute_file(path)) - def add_preserved_path_segment(self, infile: FileMaybeInTargetPrivateDir, outfiles: T.List[str], state: T.Union['Interpreter', 'ModuleState']) -> T.List[str]: + def add_preserved_path_segment(self, infile: FileMaybeInTargetPrivateDir, outfiles: T.List[str], environment: Environment) -> T.List[str]: result: T.List[str] = [] - in_abs = infile.absolute_path(state.environment.source_dir, state.environment.build_dir) + in_abs = infile.absolute_path(environment.source_dir, environment.build_dir) assert os.path.isabs(self.preserve_path_from) rel = os.path.relpath(in_abs, self.preserve_path_from) path_segment = os.path.dirname(rel) @@ -2111,11 +2111,11 @@ def add_preserved_path_segment(self, infile: FileMaybeInTargetPrivateDir, outfil result.append(os.path.join(path_segment, of)) return result - def add_file(self, newfile: FileMaybeInTargetPrivateDir, state: T.Union['Interpreter', 'ModuleState']) -> None: + def add_file(self, newfile: FileMaybeInTargetPrivateDir, environment: Environment) -> None: self.infilelist.append(newfile) outfiles = self.generator.get_base_outnames(newfile.fname) if self.preserve_path_from: - outfiles = self.add_preserved_path_segment(newfile, outfiles, state) + outfiles = self.add_preserved_path_segment(newfile, outfiles, environment) self.outfilelist += outfiles self.outmap[newfile] = outfiles From 0d4181c949b7b185a1a31e08b09509f291373841 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 4 Oct 2025 12:47:50 +0200 Subject: [PATCH 4/8] build: store Environment in Generator Objects like targets already store the environment in which they were created, do the same for Generator: pass it to the constructor and use it in process_files. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 3 ++- mesonbuild/build.py | 10 ++++++---- mesonbuild/interpreter/interpreter.py | 2 +- mesonbuild/modules/_qt.py | 4 ++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index a5995e6b18ac..b2d4e5a00da3 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -2034,7 +2034,8 @@ def compiler_to_generator(self, target: build.BuildTarget, exe = programs.ExternalProgram(compiler.get_exe()) args = compiler.get_exe_args() commands = self.compiler_to_generator_args(target, compiler) - generator = build.Generator(exe, args + commands.to_native(), + generator = build.Generator(self.environment, + exe, args + commands.to_native(), [output_templ], depfile='@PLAINNAME@.d', depends=depends) return generator.process_files(sources, self.interpreter) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1cba2f6b466c..c1a92e2ba514 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1988,7 +1988,8 @@ def __str__(self) -> str: return self.fname class Generator(HoldableObject): - def __init__(self, exe: T.Union['Executable', programs.ExternalProgram], + def __init__(self, env: Environment, + exe: T.Union['Executable', programs.ExternalProgram], arguments: T.List[str], output: T.List[str], # how2dataclass @@ -1997,6 +1998,7 @@ def __init__(self, exe: T.Union['Executable', programs.ExternalProgram], capture: bool = False, depends: T.Optional[T.List[BuildTargetTypes]] = None, name: str = 'Generator'): + self.environment = env self.exe = exe self.depfile = depfile self.capture = capture @@ -2052,17 +2054,17 @@ def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], output.depends.add(e) fs = [FileInTargetPrivateDir(f) for f in e.get_outputs()] elif isinstance(e, str): - fs = [File.from_source_file(state.environment.source_dir, state.subdir, e)] + fs = [File.from_source_file(self.environment.source_dir, state.subdir, e)] else: fs = [e] for f in fs: if preserve_path_from: - abs_f = f.absolute_path(state.environment.source_dir, state.environment.build_dir) + abs_f = f.absolute_path(self.environment.source_dir, self.environment.build_dir) if not is_parent_path(preserve_path_from, abs_f): raise InvalidArguments('generator.process: When using preserve_path_from, all input files must be in a subdirectory of the given dir.') f = FileMaybeInTargetPrivateDir(f) - output.add_file(f, state.environment) + output.add_file(f, self.environment) return output diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 8481f36bdb08..537f2768a0a7 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2228,7 +2228,7 @@ def func_generator(self, node: mparser.FunctionNode, if '@OUTPUT@' in o: raise InvalidArguments('Tried to use @OUTPUT@ in a rule with more than one output.') - return build.Generator(args[0], **kwargs) + return build.Generator(self.environment, args[0], **kwargs) @typed_pos_args('benchmark', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File, build.CustomTarget, build.CustomTargetIndex)) @typed_kwargs('benchmark', *TEST_KWS) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index a02dd3b7a420..650523904cb7 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -514,6 +514,7 @@ def _compile_ui_impl(self, state: ModuleState, kwargs: UICompilerKwArgs) -> buil preserve_path_from = os.path.join(state.source_root, state.subdir) if kwargs['preserve_paths'] else None gen = build.Generator( + state.environment, self.tools['uic'], kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'], ['ui_@BASENAME@.h'], @@ -589,6 +590,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. if do_output_json: header_gen_output.append('moc_@BASENAME@.cpp.json') moc_gen = build.Generator( + state.environment, self.tools['moc'], arguments, header_gen_output, depfile='moc_@BASENAME@.cpp.d', name=f'Qt{self.qt_version} moc header') @@ -598,6 +600,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. if do_output_json: source_gen_output.append('@BASENAME@.moc.json') moc_gen = build.Generator( + state.environment, self.tools['moc'], arguments, source_gen_output, depfile='@BASENAME@.moc.d', name=f'Qt{self.qt_version} moc source') @@ -892,6 +895,7 @@ def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> command_args.append('@INPUT@') cache_gen = build.Generator( + state.environment, self.tools['qmlcachegen'], command_args, [f'{target_name}_@BASENAME@.cpp'], From 06976218a893cd4c59cc31391e8708a2a4ba115a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 4 Oct 2025 12:50:09 +0200 Subject: [PATCH 5/8] build: do not pass Interpreter to Generator.process_files All that is needed is the subdir, pass it explicitly (and make it an optional argument so that the backend does not have to pass it). Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 +- mesonbuild/build.py | 7 +++---- mesonbuild/interpreter/interpreterobjects.py | 2 +- mesonbuild/modules/_qt.py | 8 ++++---- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b2d4e5a00da3..31082ddc6f26 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -2038,7 +2038,7 @@ def compiler_to_generator(self, target: build.BuildTarget, exe, args + commands.to_native(), [output_templ], depfile='@PLAINNAME@.d', depends=depends) - return generator.process_files(sources, self.interpreter) + return generator.process_files(sources) def compile_target_to_generator(self, target: build.CompileTarget) -> build.GeneratedList: all_sources = T.cast('_ALL_SOURCES_TYPE', target.sources) + T.cast('_ALL_SOURCES_TYPE', target.generated) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c1a92e2ba514..df01282fbb0b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -47,7 +47,6 @@ from .interpreterbase import SubProject from .linkers.linkers import StaticLinker from .mesonlib import ExecutableSerialisation, FileMode, FileOrString - from .modules import ModuleState from .mparser import BaseNode from .interpreter.kwargs import RustAbi @@ -2033,13 +2032,13 @@ def get_arglist(self, inname: str) -> T.List[str]: return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist] def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], - state: T.Union['Interpreter', 'ModuleState'], + subdir: str = '', preserve_path_from: T.Optional[str] = None, extra_args: T.Optional[T.List[str]] = None, env: T.Optional[EnvironmentVariables] = None) -> 'GeneratedList': output = GeneratedList( self, - state.subdir, + subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else [], env=env if env is not None else EnvironmentVariables()) @@ -2054,7 +2053,7 @@ def process_files(self, files: T.Iterable[T.Union[str, File, GeneratedTypes]], output.depends.add(e) fs = [FileInTargetPrivateDir(f) for f in e.get_outputs()] elif isinstance(e, str): - fs = [File.from_source_file(self.environment.source_dir, state.subdir, e)] + fs = [File.from_source_file(self.environment.source_dir, subdir, e)] else: fs = [e] diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 86e8957bc2a1..d5a78c10ee65 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1153,7 +1153,7 @@ def process_method(self, 'Calling generator.process with CustomTarget or Index of CustomTarget.', '0.57.0', self.interpreter.subproject) - gl = self.held_object.process_files(args[0], self.interpreter, + gl = self.held_object.process_files(args[0], self.interpreter.subdir, preserve_path_from, extra_args=kwargs['extra_args'], env=kwargs['env']) return gl diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index 650523904cb7..ef3193b9ce98 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -519,7 +519,7 @@ def _compile_ui_impl(self, state: ModuleState, kwargs: UICompilerKwArgs) -> buil kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'], ['ui_@BASENAME@.h'], name=f'Qt{self.qt_version} ui') - return gen.process_files(kwargs['sources'], state, preserve_path_from) + return gen.process_files(kwargs['sources'], state.subdir, preserve_path_from) @FeatureNew('qt.compile_moc', '0.59.0') @noPosargs @@ -594,7 +594,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. self.tools['moc'], arguments, header_gen_output, depfile='moc_@BASENAME@.cpp.d', name=f'Qt{self.qt_version} moc header') - output.append(moc_gen.process_files(kwargs['headers'], state, preserve_path_from)) + output.append(moc_gen.process_files(kwargs['headers'], state.subdir, preserve_path_from)) if kwargs['sources']: source_gen_output: T.List[str] = ['@BASENAME@.moc'] if do_output_json: @@ -604,7 +604,7 @@ def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T. self.tools['moc'], arguments, source_gen_output, depfile='@BASENAME@.moc.d', name=f'Qt{self.qt_version} moc source') - output.append(moc_gen.process_files(kwargs['sources'], state, preserve_path_from)) + output.append(moc_gen.process_files(kwargs['sources'], state.subdir, preserve_path_from)) return output @@ -902,7 +902,7 @@ def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> name=f'Qml cache generation for {target_name}') output: T.List[T.Union[build.CustomTarget, build.GeneratedList]] = [] - output.append(cache_gen.process_files(kwargs['qml_sources'], state)) + output.append(cache_gen.process_files(kwargs['qml_sources'], state.subdir)) cachegen_inputs: T.List[str] = [] qml_sources_paths = self._source_to_files(state, kwargs['qml_sources']) From a2b3e1951072fe9b5af276024b530b7883bff468 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 4 Oct 2025 14:18:24 +0200 Subject: [PATCH 6/8] build, backend: store build_def_files in Build The interpreter's build_def_files are the only remaining piece of interpreter state used by the backend. Compute it once and store it in build, using a property to ensure that it's initialized and accessed correctly. This also removes one of the cases in which introspection uses the interpreter object. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 2 +- mesonbuild/backend/xcodebackend.py | 4 ++-- mesonbuild/build.py | 13 +++++++++++++ mesonbuild/interpreter/interpreter.py | 4 ++-- mesonbuild/mintro.py | 10 +++------- mesonbuild/msetup.py | 1 + 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 31082ddc6f26..28a1ee359fbd 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1303,7 +1303,7 @@ def get_regen_filelist(self) -> T.List[str]: '''List of all files whose alteration means that the build definition needs to be regenerated.''' deps = OrderedSet([str(Path(self.build_to_src) / df) - for df in self.interpreter.get_build_def_files()]) + for df in self.build.def_files]) if self.environment.is_cross_build(): deps.update(self.environment.coredata.cross_files) deps.update(self.environment.coredata.config_files) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index ac2126025398..a4290d58dec3 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -629,7 +629,7 @@ def generate_target_file_maps_impl(self, targets) -> None: self.fileref_ids[k] = self.gen_id() def generate_build_file_maps(self) -> None: - for buildfile in self.interpreter.get_build_def_files(): + for buildfile in self.build.def_files: assert isinstance(buildfile, str) self.buildfile_ids[buildfile] = self.gen_id() self.fileref_ids[buildfile] = self.gen_id() @@ -1025,7 +1025,7 @@ def generate_pbx_file_reference(self, objects_dict: PbxDict) -> None: custom_dict.add_item('sourceTree', 'SOURCE_ROOT') objects_dict.add_item(self.custom_target_output_fileref[o], custom_dict) - for buildfile in self.interpreter.get_build_def_files(): + for buildfile in self.build.def_files: basename = os.path.split(buildfile)[1] buildfile_dict = PbxDict() typestr = self.get_xcodetype(buildfile) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index df01282fbb0b..cf4f31a1af89 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -333,6 +333,7 @@ class Build: def __init__(self, environment: Environment): self.version = coredata.version + self._def_files: T.Optional[T.List[str]] = None self.project_name = 'name of master project' self.project_version: T.Optional[str] = None self.environment = environment @@ -376,6 +377,18 @@ def __init__(self, environment: Environment): Needed for tracking whether a modules options needs to be exposed to the user. """ + @property + def def_files(self) -> T.List[str]: + if self._def_files is None: + raise MesonBugException('build.def_files has not been set yet') + return self._def_files + + @def_files.setter + def def_files(self, value: T.List[str]): + if self._def_files is not None: + raise MesonBugException('build.def_files already set') + self._def_files = value + def get_build_targets(self) -> OrderedDict[str, BuildTarget]: build_targets = OrderedDict() for name, t in self.targets.items(): diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 537f2768a0a7..88b3fc19a7d0 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -536,10 +536,10 @@ def handle_meson_version_from_ast(self) -> None: if isinstance(val, mparser.StringNode): self.handle_meson_version(val.value, val) - def get_build_def_files(self) -> mesonlib.OrderedSet[str]: + def get_build_def_files(self) -> T.List[str]: if self.cargo: self.build_def_files.update(self.cargo.get_build_def_files()) - return self.build_def_files + return list(self.build_def_files) def add_build_def_file(self, f: mesonlib.FileOrString) -> None: # Use relative path for files within source directory, and absolute path diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index aeda4837ef64..57805a35c0c8 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -29,8 +29,6 @@ if T.TYPE_CHECKING: import argparse - from .interpreter import Interpreter - class IntrospectionEncoder(json.JSONEncoder): def default(self, obj: T.Any) -> T.Any: if isinstance(obj, UnknownValue): @@ -62,7 +60,6 @@ def get_meson_introspection_types(coredata: T.Optional[cdata.CoreData] = None, benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks()) testdata = backend.create_test_serialisation(builddata.get_tests()) installdata = backend.create_install_data() - interpreter = backend.interpreter else: benchmarkdata = testdata = installdata = None @@ -71,7 +68,7 @@ def get_meson_introspection_types(coredata: T.Optional[cdata.CoreData] = None, ('ast', IntroCommand('Dump the AST of the meson file', no_bd=dump_ast)), ('benchmarks', IntroCommand('List all benchmarks', func=lambda: list_benchmarks(benchmarkdata))), ('buildoptions', IntroCommand('List all build options', func=lambda: list_buildoptions(coredata), no_bd=list_buildoptions_from_source)), - ('buildsystem_files', IntroCommand('List files that make up the build system', func=lambda: list_buildsystem_files(builddata, interpreter))), + ('buildsystem_files', IntroCommand('List files that make up the build system', func=lambda: list_buildsystem_files(builddata))), ('compilers', IntroCommand('List used compilers', func=lambda: list_compilers(coredata))), ('dependencies', IntroCommand('List external dependencies', func=lambda: list_deps(coredata, backend), no_bd=list_deps_from_source)), ('scan_dependencies', IntroCommand('Scan for dependencies used in the meson.build file', no_bd=list_deps_from_source)), @@ -340,10 +337,9 @@ def find_buildsystem_files_list(src_dir: str) -> T.List[str]: for f in build_files.intersection(files)) return filelist -def list_buildsystem_files(builddata: build.Build, interpreter: Interpreter) -> T.List[str]: +def list_buildsystem_files(builddata: build.Build) -> T.List[str]: src_dir = builddata.environment.get_source_dir() - filelist = list(interpreter.get_build_def_files()) - filelist = [PurePath(src_dir, x).as_posix() for x in filelist] + filelist = [PurePath(src_dir, x).as_posix() for x in builddata.def_files] return filelist def list_compilers(coredata: cdata.CoreData) -> T.Dict[str, T.Dict[str, T.Dict[str, str]]]: diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index f1fa777d179a..0e579f0d9741 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -340,6 +340,7 @@ def finalize_postconf_hooks(self, b: build.Build, intr: interpreter.Interpreter) b.devenv.append(intr.backend.get_devenv()) for mod in intr.modules.values(): mod.postconf_hook(b) + b.def_files = intr.get_build_def_files() def run_genvslite_setup(options: CMDOptions) -> None: # With --genvslite, we essentially want to invoke multiple 'setup' iterations. I.e. - From a874ace77f818d6030f81301fbbb40fb1030df01 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 4 Oct 2025 14:21:50 +0200 Subject: [PATCH 7/8] intro: compute meson_variables for dependencies at setup time Another place where the interpreter is accessed surreptitiously is dependency introspection, which looks at the variables. Do that at setup time instead while the interpreter is alive. Signed-off-by: Paolo Bonzini --- mesonbuild/dependencies/base.py | 1 + mesonbuild/mintro.py | 18 +++++------------- mesonbuild/msetup.py | 8 ++++++++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 72dbfdf2d168..01175291e4c2 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -128,6 +128,7 @@ def __init__(self, type_name: DependencyTypeName, kwargs: T.Dict[str, T.Any]) -> self.d_features: T.DefaultDict[str, T.List[T.Any]] = collections.defaultdict(list) self.featurechecks: T.List['FeatureCheckBase'] = [] self.feature_since: T.Optional[T.Tuple[str, str]] = None + self.meson_variables: T.List[str] = [] def __eq__(self, other: object) -> bool: if not isinstance(other, Dependency): diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 57805a35c0c8..b1e0941c88af 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -22,13 +22,14 @@ from . import build, environment, mesonlib, options, coredata as cdata from .ast import IntrospectionInterpreter, AstConditionLevel, AstIDGenerator, AstIndentationGenerator, AstJSONPrinter from .backend import backends -from .dependencies import Dependency -from .interpreterbase import ObjectHolder, UnknownValue +from .interpreterbase import UnknownValue from .options import OptionKey if T.TYPE_CHECKING: import argparse + from .dependencies import Dependency + class IntrospectionEncoder(json.JSONEncoder): def default(self, obj: T.Any) -> T.Any: if isinstance(obj, UnknownValue): @@ -385,7 +386,7 @@ def _src_to_str(src_file: T.Union[mesonlib.FileOrString, build.GeneratedTypes, b return [f for s in src_file.as_list() for f in _src_to_str(s)] raise mesonlib.MesonBugException(f'Invalid file type {type(src_file)}.') - def _create_result(d: Dependency, varname: T.Optional[str] = None) -> T.Dict[str, T.Any]: + def _create_result(d: Dependency) -> T.Dict[str, T.Any]: return { 'name': d.name, 'type': d.type_name, @@ -397,22 +398,13 @@ def _create_result(d: Dependency, varname: T.Optional[str] = None) -> T.Dict[str 'extra_files': [f for s in d.get_extra_files() for f in _src_to_str(s)], 'dependencies': [e.name for e in d.ext_deps], 'depends': [lib.get_id() for lib in getattr(d, 'libraries', [])], - 'meson_variables': [varname] if varname else [], + 'meson_variables': d.meson_variables, } for d in coredata.deps.host.values(): if d.found(): result[d.name] = _create_result(d) - for varname, holder in backend.interpreter.variables.items(): - if isinstance(holder, ObjectHolder): - d = holder.held_object - if isinstance(d, Dependency) and d.found(): - if d.name in result: - T.cast('T.List[str]', result[d.name]['meson_variables']).append(varname) - else: - result[d.name] = _create_result(d, varname) - return list(result.values()) def get_test_list(testdata: T.List[backends.TestSerialisation]) -> T.List[T.Dict[str, T.Union[str, int, T.List[str], T.Dict[str, str]]]]: diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 0e579f0d9741..179ff3fa2db3 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -10,7 +10,9 @@ import typing as T from . import build, coredata, environment, interpreter, mesonlib, mintro, mlog +from .dependencies import Dependency from .mesonlib import MesonException +from .interpreterbase import ObjectHolder from .options import OptionKey if T.TYPE_CHECKING: @@ -337,6 +339,12 @@ def _generate(self, env: environment.Environment, capture: bool, vslite_ctx: T.O return captured_compile_args def finalize_postconf_hooks(self, b: build.Build, intr: interpreter.Interpreter) -> None: + for varname, holder in intr.variables.items(): + if isinstance(holder, ObjectHolder): + d = holder.held_object + if isinstance(d, Dependency) and d.found(): + d.meson_variables.append(varname) + b.devenv.append(intr.backend.get_devenv()) for mod in intr.modules.values(): mod.postconf_hook(b) From 7a5231078876242ccf6a477b3f01f62f231bf53d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 4 Oct 2025 14:26:55 +0200 Subject: [PATCH 8/8] backend: remove Interpreter No one is accessing backend.interpreter anymore, get rid of it to avoid future temptations. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/backends.py | 33 +++++++++++++-------------- mesonbuild/backend/ninjabackend.py | 5 ++-- mesonbuild/backend/vs2010backend.py | 21 ++++++++--------- mesonbuild/backend/vs2012backend.py | 5 ++-- mesonbuild/backend/vs2013backend.py | 5 ++-- mesonbuild/backend/vs2015backend.py | 4 +--- mesonbuild/backend/vs2017backend.py | 5 ++-- mesonbuild/backend/vs2019backend.py | 5 ++-- mesonbuild/backend/vs2022backend.py | 5 ++-- mesonbuild/backend/xcodebackend.py | 5 ++-- mesonbuild/interpreter/interpreter.py | 4 ++-- 11 files changed, 43 insertions(+), 54 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 28a1ee359fbd..19f856b68cfd 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -39,7 +39,7 @@ from ..arglist import CompilerArgs from ..compilers import Compiler from ..environment import Environment - from ..interpreter import Interpreter, Test + from ..interpreter import Test from ..linkers.linkers import StaticLinker from ..mesonlib import FileMode, FileOrString from ..options import ElementaryOptionValues @@ -217,47 +217,47 @@ def __post_init__(self) -> None: assert isinstance(self.exe_wrapper, programs.ExternalProgram) -def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None, interpreter: T.Optional['Interpreter'] = None) -> T.Optional['Backend']: +def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None) -> T.Optional['Backend']: if backend == 'ninja': from . import ninjabackend - return ninjabackend.NinjaBackend(build, interpreter) + return ninjabackend.NinjaBackend(build) elif backend == 'vs': from . import vs2010backend - return vs2010backend.autodetect_vs_version(build, interpreter) + return vs2010backend.autodetect_vs_version(build) elif backend == 'vs2010': from . import vs2010backend - return vs2010backend.Vs2010Backend(build, interpreter) + return vs2010backend.Vs2010Backend(build) elif backend == 'vs2012': from . import vs2012backend - return vs2012backend.Vs2012Backend(build, interpreter) + return vs2012backend.Vs2012Backend(build) elif backend == 'vs2013': from . import vs2013backend - return vs2013backend.Vs2013Backend(build, interpreter) + return vs2013backend.Vs2013Backend(build) elif backend == 'vs2015': from . import vs2015backend - return vs2015backend.Vs2015Backend(build, interpreter) + return vs2015backend.Vs2015Backend(build) elif backend == 'vs2017': from . import vs2017backend - return vs2017backend.Vs2017Backend(build, interpreter) + return vs2017backend.Vs2017Backend(build) elif backend == 'vs2019': from . import vs2019backend - return vs2019backend.Vs2019Backend(build, interpreter) + return vs2019backend.Vs2019Backend(build) elif backend == 'vs2022': from . import vs2022backend - return vs2022backend.Vs2022Backend(build, interpreter) + return vs2022backend.Vs2022Backend(build) elif backend == 'xcode': from . import xcodebackend - return xcodebackend.XCodeBackend(build, interpreter) + return xcodebackend.XCodeBackend(build) elif backend == 'none': from . import nonebackend - return nonebackend.NoneBackend(build, interpreter) + return nonebackend.NoneBackend(build) return None -def get_genvslite_backend(genvsname: str, build: T.Optional[build.Build] = None, interpreter: T.Optional['Interpreter'] = None) -> T.Optional['Backend']: +def get_genvslite_backend(genvsname: str, build: T.Optional[build.Build] = None) -> T.Optional['Backend']: if genvsname == 'vs2022': from . import vs2022backend - return vs2022backend.Vs2022Backend(build, interpreter, gen_lite = True) + return vs2022backend.Vs2022Backend(build, gen_lite = True) return None # This class contains the basic functionality that is needed by all backends. @@ -267,14 +267,13 @@ class Backend: environment: T.Optional['Environment'] name = '' - def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional['Interpreter']): + def __init__(self, build: T.Optional[build.Build]): # Make it possible to construct a dummy backend # This is used for introspection without a build directory if build is None: self.environment = None return self.build = build - self.interpreter = interpreter self.environment = build.environment self.processed_targets: T.Set[str] = set() self.build_dir = self.environment.get_build_dir() diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 166b118e3953..3fd82a85be63 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -41,7 +41,6 @@ from .._typing import ImmutableListProtocol from ..build import ExtractedObjects, LibTypes - from ..interpreter import Interpreter from ..linkers.linkers import DynamicLinker, StaticLinker from ..compilers.cs import CsCompiler from ..compilers.fortran import FortranCompiler @@ -479,8 +478,8 @@ def to_json(self) -> T.Dict[str, object]: class NinjaBackend(backends.Backend): - def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]): - super().__init__(build, interpreter) + def __init__(self, build: T.Optional[build.Build]): + super().__init__(build) self.name = 'ninja' self.ninja_filename = 'build.ninja' self.fortran_deps: T.Dict[str, T.Dict[str, File]] = {} diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index adcb2543e882..be88eb7fd41e 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -28,11 +28,10 @@ if T.TYPE_CHECKING: from ..arglist import CompilerArgs - from ..interpreter import Interpreter Project = T.Tuple[str, Path, str, MachineChoice] -def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]) -> backends.Backend: +def autodetect_vs_version(build: T.Optional[build.Build]) -> backends.Backend: vs_version = os.getenv('VisualStudioVersion', None) vs_install_dir = os.getenv('VSINSTALLDIR', None) if not vs_install_dir: @@ -42,27 +41,27 @@ def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optiona # vcvarsall.bat doesn't set it, so also use VSINSTALLDIR if vs_version == '11.0' or 'Visual Studio 11' in vs_install_dir: from mesonbuild.backend.vs2012backend import Vs2012Backend - return Vs2012Backend(build, interpreter) + return Vs2012Backend(build) if vs_version == '12.0' or 'Visual Studio 12' in vs_install_dir: from mesonbuild.backend.vs2013backend import Vs2013Backend - return Vs2013Backend(build, interpreter) + return Vs2013Backend(build) if vs_version == '14.0' or 'Visual Studio 14' in vs_install_dir: from mesonbuild.backend.vs2015backend import Vs2015Backend - return Vs2015Backend(build, interpreter) + return Vs2015Backend(build) if vs_version == '15.0' or 'Visual Studio 17' in vs_install_dir or \ 'Visual Studio\\2017' in vs_install_dir: from mesonbuild.backend.vs2017backend import Vs2017Backend - return Vs2017Backend(build, interpreter) + return Vs2017Backend(build) if vs_version == '16.0' or 'Visual Studio 19' in vs_install_dir or \ 'Visual Studio\\2019' in vs_install_dir: from mesonbuild.backend.vs2019backend import Vs2019Backend - return Vs2019Backend(build, interpreter) + return Vs2019Backend(build) if vs_version == '17.0' or 'Visual Studio 22' in vs_install_dir or \ 'Visual Studio\\2022' in vs_install_dir: from mesonbuild.backend.vs2022backend import Vs2022Backend - return Vs2022Backend(build, interpreter) + return Vs2022Backend(build) if 'Visual Studio 10.0' in vs_install_dir: - return Vs2010Backend(build, interpreter) + return Vs2010Backend(build) raise MesonException('Could not detect Visual Studio using VisualStudioVersion: {!r} or VSINSTALLDIR: {!r}!\n' 'Please specify the exact backend to use.'.format(vs_version, vs_install_dir)) @@ -135,8 +134,8 @@ class Vs2010Backend(backends.Backend): name = 'vs2010' - def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter], gen_lite: bool = False): - super().__init__(build, interpreter) + def __init__(self, build: T.Optional[build.Build], gen_lite: bool = False): + super().__init__(build) self.project_file_version = '10.0.30319.1' self.sln_file_version = '11.00' self.sln_version_comment = '2010' diff --git a/mesonbuild/backend/vs2012backend.py b/mesonbuild/backend/vs2012backend.py index 922cd60d49d9..5ed31d92cde1 100644 --- a/mesonbuild/backend/vs2012backend.py +++ b/mesonbuild/backend/vs2012backend.py @@ -10,14 +10,13 @@ if T.TYPE_CHECKING: from ..build import Build - from ..interpreter import Interpreter class Vs2012Backend(Vs2010Backend): name = 'vs2012' - def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]): - super().__init__(build, interpreter) + def __init__(self, build: T.Optional[Build]): + super().__init__(build) self.vs_version = '2012' self.sln_file_version = '12.00' self.sln_version_comment = '2012' diff --git a/mesonbuild/backend/vs2013backend.py b/mesonbuild/backend/vs2013backend.py index cf5d5980e8bb..c53e64c0cc23 100644 --- a/mesonbuild/backend/vs2013backend.py +++ b/mesonbuild/backend/vs2013backend.py @@ -9,14 +9,13 @@ if T.TYPE_CHECKING: from ..build import Build - from ..interpreter import Interpreter class Vs2013Backend(Vs2010Backend): name = 'vs2013' - def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]): - super().__init__(build, interpreter) + def __init__(self, build: T.Optional[Build]): + super().__init__(build) self.vs_version = '2013' self.sln_file_version = '12.00' self.sln_version_comment = '2013' diff --git a/mesonbuild/backend/vs2015backend.py b/mesonbuild/backend/vs2015backend.py index 1862def1abba..f5a468f6fbc5 100644 --- a/mesonbuild/backend/vs2015backend.py +++ b/mesonbuild/backend/vs2015backend.py @@ -10,14 +10,12 @@ if T.TYPE_CHECKING: from ..build import Build - from ..interpreter import Interpreter class Vs2015Backend(Vs2010Backend): name = 'vs2015' - def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]): - super().__init__(build, interpreter) + def __init__(self, build: T.Optional[Build]): self.vs_version = '2015' self.sln_file_version = '12.00' self.sln_version_comment = '14' diff --git a/mesonbuild/backend/vs2017backend.py b/mesonbuild/backend/vs2017backend.py index 372e1ce0d097..bf6c33771f16 100644 --- a/mesonbuild/backend/vs2017backend.py +++ b/mesonbuild/backend/vs2017backend.py @@ -12,15 +12,14 @@ if T.TYPE_CHECKING: from ..build import Build - from ..interpreter import Interpreter class Vs2017Backend(Vs2010Backend): name = 'vs2017' - def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]): - super().__init__(build, interpreter) + def __init__(self, build: T.Optional[Build]): + super().__init__(build) self.vs_version = '2017' self.sln_file_version = '12.00' self.sln_version_comment = '15' diff --git a/mesonbuild/backend/vs2019backend.py b/mesonbuild/backend/vs2019backend.py index 61ad75d5ea7e..bf86a4701730 100644 --- a/mesonbuild/backend/vs2019backend.py +++ b/mesonbuild/backend/vs2019backend.py @@ -11,15 +11,14 @@ if T.TYPE_CHECKING: from ..build import Build - from ..interpreter import Interpreter class Vs2019Backend(Vs2010Backend): name = 'vs2019' - def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter]): - super().__init__(build, interpreter) + def __init__(self, build: T.Optional[Build]): + super().__init__(build) self.sln_file_version = '12.00' self.sln_version_comment = 'Version 16' diff --git a/mesonbuild/backend/vs2022backend.py b/mesonbuild/backend/vs2022backend.py index ca449a4e57f8..d7de4be32ba9 100644 --- a/mesonbuild/backend/vs2022backend.py +++ b/mesonbuild/backend/vs2022backend.py @@ -11,15 +11,14 @@ if T.TYPE_CHECKING: from ..build import Build - from ..interpreter import Interpreter class Vs2022Backend(Vs2010Backend): name = 'vs2022' - def __init__(self, build: T.Optional[Build], interpreter: T.Optional[Interpreter], gen_lite: bool = False): - super().__init__(build, interpreter, gen_lite=gen_lite) + def __init__(self, build: T.Optional[Build], gen_lite: bool = False): + super().__init__(build, gen_lite=gen_lite) self.sln_file_version = '12.00' self.sln_version_comment = 'Version 17' diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index a4290d58dec3..7c040a613cf4 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -17,7 +17,6 @@ if T.TYPE_CHECKING: from ..build import BuildTarget from ..compilers import Compiler - from ..interpreter import Interpreter INDENT = '\t' XCODETYPEMAP = {'c': 'sourcecode.c.c', @@ -236,8 +235,8 @@ class XCodeBackend(backends.Backend): name = 'xcode' - def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]): - super().__init__(build, interpreter) + def __init__(self, build: T.Optional[build.Build]): + super().__init__(build) self.project_uid = self.environment.coredata.lang_guids['default'].replace('-', '')[:24] self.buildtype = T.cast('str', self.environment.coredata.optstore.get_value_for(OptionKey('buildtype'))) self.project_conflist = self.gen_id() diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 88b3fc19a7d0..d3a9142fea71 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1158,10 +1158,10 @@ def set_backend(self) -> None: # Use of the '--genvslite vsxxxx' option ultimately overrides any '--backend xxx' # option the user may specify. backend_name = self.coredata.optstore.get_value_for(OptionKey('genvslite')) - self.backend = backends.get_genvslite_backend(backend_name, self.build, self) + self.backend = backends.get_genvslite_backend(backend_name, self.build) else: backend_name = self.coredata.optstore.get_value_for(OptionKey('backend')) - self.backend = backends.get_backend_from_name(backend_name, self.build, self) + self.backend = backends.get_backend_from_name(backend_name, self.build) if self.backend is None: raise InterpreterException(f'Unknown backend "{backend_name}".')