Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,6 @@ def should_use_rspfile(self, element: NinjaBuildElement) -> bool:
element.elems) >= rsp_threshold

class NinjaBuildElement:

rule: NinjaRule

def __init__(self, all_outputs: T.Set[str], outfilenames: ListifiedStr, rulename: str, infilenames: ListifiedStr, implicit_outs: T.Optional[T.List[str]] = None):
self.implicit_outfilenames = implicit_outs or []
if isinstance(outfilenames, str):
Expand All @@ -334,6 +331,7 @@ def __init__(self, all_outputs: T.Set[str], outfilenames: ListifiedStr, rulename
self.elems: T.List[T.Tuple[str, T.List[str]]] = []
self.all_outputs = all_outputs
self.output_errors = ''
self.rule: NinjaRule | None = None

@eli-schwartz eli-schwartz Aug 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later code assumes that it cannot be None, so this is type-unsafe.

The current intended use of this code is that any time a NinjaBuildElement is created, NinjaBackend.add_build(self, build) is called, and sets build.rule = self.ruledict[build.rulename].

The type system doesn't have a good way to describe "the function isn't completely initialized by __init__()" but saying that the property is nullable isn't the correct way to handle this. At least not without more work. Nullable != "uninitialized".

Since commit 739e86f (your previous commit) all call sites for self.rule.* properties occur after a code flow that checks self._should_use_rspfile but I'm not convinced that's actually a good way to uphold this invariant. We could easily add other code accessing self.rule by accident etc.

Perhaps we should be setting it in __init__ rather than adding it after the fact.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type system doesn't have a good way to describe "the function isn't completely initialized by init()" but saying that the property is nullable isn't the correct way to handle this. At least not without more work. Nullable != "uninitialized".

We have one in mesonlib:

rule: mesonlib.late_property[NinjaRule] = mesonlib.late_property()

A bit of a mouthful to declare it, but it works and it allows removing the if self.rule statement in _should_use_rspfile.

Setting it in __init__ requires passing the NinjaBuild, which is a largeish change and the late_property alternative is easier.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, thanks. I forgot we added that!


def add_dep(self, dep: ListifiedStr) -> None:
if isinstance(dep, list):
Expand All @@ -359,6 +357,9 @@ def add_item(self, name: str, elems: T.Union[ListifiedStr, CompilerArgs]) -> Non
if name == 'DEPFILE':
self.elems.append((name + '_UNQUOTED', elems))

def remove_item(self, name: str) -> None:
self.elems[:] = [e for e in self.elems if e[0] != name]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make self.elems a dictionary instead, then you can just use del self.elems[name].

As an extra benefit add_item can also check if the name is already present and raise a MesonBugException.


@mesonlib.lazy_property
def _should_use_rspfile(self) -> bool:
# 'phony' is a rule built-in to ninja
Expand Down Expand Up @@ -3390,7 +3391,12 @@ def quote_make_target(targetName: str) -> str:
result += c
return result
element.add_item('CUDA_ESCAPED_TARGET', quote_make_target(rel_obj))
element.add_item('ARGS', commands)

# NinjaRule.should_use_rspfile counts element.elems too, which will
# exceed the RSP threshold only after added
if self.ninja.should_use_rspfile(element) and compiler.rsp_file_syntax() == RSPFileSyntax.NASM:
element.remove_item('ARGS')
Comment on lines +3396 to +3399

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems correct -- calculating this outside would be a waste of time, anyway.

exe = compiler.get_exelist()
# Add to commands the args created by generate_compile_rule_for().
# commands remain separate from exelist because they must stay
Expand All @@ -3409,8 +3415,6 @@ def quote_make_target(targetName: str) -> str:
cmd_type = f' (wrapped by meson {reason})' if reason else ''
element.add_item('COMMAND', meson_exe_cmd)
element.add_item('description', f'Compiling {compiler.get_display_language()} object {rel_obj}{cmd_type}')
else:
element.add_item('ARGS', commands)

self.add_dependency_scanner_entries_to_element(target, compiler, element, src)
self.add_build(element)
Expand Down
2 changes: 1 addition & 1 deletion mesonbuild/cmake/traceparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ def handle_working_dir(key: str, target: CMakeGeneratorTarget) -> None:

for i in args:
if i in magic_keys:
if i == 'OUTPUT':
if i in ('OUTPUT', 'BYPRODUCTS'):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pylint asks to use {'OUTPUT', 'BYPRODUCTS'}.

fn = handle_output
elif i == 'DEPENDS':
fn = handle_depends
Expand Down
Loading