Skip to content

type safe option value getters - #15678

Draft
dcbaker wants to merge 24 commits into
mesonbuild:masterfrom
dcbaker:submit/type-safe-option-getters
Draft

type safe option value getters#15678
dcbaker wants to merge 24 commits into
mesonbuild:masterfrom
dcbaker:submit/type-safe-option-getters

Conversation

@dcbaker

@dcbaker dcbaker commented Apr 3, 2026

Copy link
Copy Markdown
Member

This adds a couple of methods to the OptionStore to work with options value in a type safe manner. This works by putting the type in the getter itself, this avoids the need for the pattern of:

opt = optstore.get_value_for('b_foo')
assert isinstance(opt, str), 'for mypy'

While also giving better error messages. Additionally, I've added a default= parameter, to allow not erroring when an options isn't available (a per-platform or per-compiler option). The end result is the following:

  • get_value_for a type safe getter for a system value
  • get_value_for_untyped a type unsafe getter for system values, works like get_value_for in current main
  • get_value_for_target a type safe getter for an option that could be overwritten by a target
  • get_value_for_target_untyped a type unsafe getter for an option that could be overwritten by a target, same as get_value_for_target in current main branch
  • get_value_for_maybe_target a type safe getter that allows a target to be optional. This is a bit ugly, but I don't see a better way to handle this without duplicating code in a way that would lead to bugs.

this allows the following patterns to be eliminated:

opt = optstore.get_value_for_untyped(OptionKey('b_foo'))
assert isinstance(opt, str), 'for mypy'

with

opt = opstore.get_value_for(OptionKey('b_foo'), str)

(and the same for targets)

and

key = OptionKey('b_foo')
if target is not None:
    opt = opstore.get_value_for_target(target, key, str)
else:
    opt = opstore.get_value_for(key, str)

with

opt = optstore.get_value_for_maybe_target(target, OptionKey('b_foo'), str)

and

try:
    opt = opstore.get_value_for(OptionKey('b_foo'), bool)
except KeyError:
    opt = False

with

opt = optstore.get_value_for(OptionKey('b_foo'), bool, default=False)

This has been set to draft as I'm sure there will be some issues with Windows and the VS backend that need to be resolved.

@dcbaker
dcbaker force-pushed the submit/type-safe-option-getters branch from baed5ae to 12bbf92 Compare April 6, 2026 19:56
@bonzini bonzini added this to the 1.12 milestone Jun 8, 2026
@dcbaker
dcbaker force-pushed the submit/type-safe-option-getters branch 5 times, most recently from ba13fc2 to 5bae175 Compare June 11, 2026 16:59
@dcbaker
dcbaker force-pushed the submit/type-safe-option-getters branch 4 times, most recently from b8add99 to 3600c7b Compare June 22, 2026 16:07
@dcbaker
dcbaker force-pushed the submit/type-safe-option-getters branch 3 times, most recently from 138f9b6 to 455874d Compare June 22, 2026 21:32
dcbaker added 15 commits June 22, 2026 14:55
And do some cleanups.

This one test takes a ton of time to run, and cannot be parallelized. I
have split the code so that we run one test per language, but the rest
of the subtests remain. This allows us to run up to 11 parallel tests
for templates, which can really help with the test runtime.
This leaves a wrapper in the coredata struct, which will allow us to
have one big mechanical change in the next commit
This is mostly generated through search and replace, but with the
wrapper in CoreData removed manually
Leaves a wrapper in place that will be removed later
These provide unconstrained ElementryOptionValues variants. The next
patches will provide typed versions using the normal name, that do
automatic type validation.
These use a TypeVar to constrain the type, and then do a runtime check
to ensure that the option is of the correct type.

One has not been added for `get_option_and_value_for` because we need to
further constrain the types via overloads, but I can't figure out how to
make the type checker happy with it.
This allows use to replace patterns like:
```python
if key in opstore and opstore.get_value_for(...)
```
with
```python
if opstore.get_value_for(..., default=...)
```
This replaces most of the uses of the unsafe get_value_for, with a
couple of exceptions:

- There is code in the Compiler that does some interesting BuildTarget |
  None checking that should be wrapped
- the `install_umask` option cannot use this helper due to it's variant
  type
- There's a wrapper in the Module code that needs to be replaced
There are still a few uses of the unsafe variant, manly around compilers
and some wrappers that need to be removed. Additionally, this work
allows some cleanups to remove try/except blocks
dcbaker added 9 commits June 22, 2026 14:55
These are allowed to take None for target. This is the only case where
the subproject argument is not `target.subproject`.

To simplify this, I have implemented the signature such that a target or
a SubProject (which includes None) can be passed, a small helper is used
to convert the union into a tuple of BuildTarget | None, SubProject |
None. This simplifies the implementation, fixes the typing, and is much
cleaner than a previous approach that used type overloading.
There is some added complexity because the langauge of an option was
merged back into the name of the key instead of being a separate field.

This method was basically doing the same thing as
get_option_for_maybe_target, plus building a key. Some of the uses
didn't make sense, as we knew we didn't have a target and just wanted a
global value, plus we lost our type checking.
It was just another layer of indirection, and doesn't provide type
safety
This is just a wrapper around `get_value_for_target` with `default=False`.
Checking that the option is in the command line arguments is less robust
than checking that the Option has a non default value.
@dcbaker
dcbaker force-pushed the submit/type-safe-option-getters branch from 455874d to 733f7c8 Compare June 22, 2026 21:55
@dcbaker

dcbaker commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

There's a bug in "compilers: Fix the signature of get_option_{std,compile,link}_args", but I can't find it immediately by inspection

@bonzini bonzini modified the milestones: 1.12, 1.13 Jun 23, 2026

try:
host_compiler_args = self.host_compiler.get_option_compile_args(target, subproject)
host_compiler_args = self.host_compiler.get_option_compile_args(target)

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.

I think this needs to be target if target is not None else subproject.

@bonzini

bonzini commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

The failing test logs (test cases\windows\14 resources with custom target depend_files):

[1/4] "C:\hostedtoolcache\windows\Python\3.10.11\x64\python.exe" "D:\a\1\s\test cases\windows\14 resources with custom target depend_files\ico\gen-ico.py" "../test cases/windows/14 resources with custom target depend_files/ico/sample.ico.in" "ico/sample.ico"
[2/4] "cl" "-Iprog.exe.p" "-I." "-I..\test cases\windows\14 resources with custom target depend_files" "-Ires" "/MDd" "/nologo" "/showIncludes" "/utf-8" "/W2" "/Od" "/Z7" "/Fdprog.exe.p\prog.c.pdb" /Foprog.exe.p/prog.c.obj "/c" "../test cases/windows/14 resources with custom target depend_files/prog.c"
[3/4] "C:\hostedtoolcache\windows\Python\3.10.11\x64\python.exe" "D:/a/1/s/meson.py" "--internal" "rc" "--rc" "C:/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0//x64/rc.exe" "--cl" "cl" "--Xarg=/showIncludes" "--Xarg=/EP" "--Xarg=/nologo" "--Xarg=/DRC_INVOKED" "--Xarg=/Tc../test cases/windows/14 resources with custom target depend_files/res/myres.rc" "-ID:/a/1/s/b 3d59bc125c/ico" "/nologo" "/fores/res_myres.rc_myres.res" "../test cases/windows/14 resources with custom target depend_files/res/myres.rc"
myres.rc
myres.rc
[4/4] "link"  /MACHINE:x64 /OUT:prog.exe res/res_myres.rc_myres.res prog.exe.p/prog.c.obj "/release" "/nologo" "/DEBUG" "/PDB:prog.pdb" "/SUBSYSTEM:WINDOWS"
FAILED: prog.exe prog.pdb 
"link"  /MACHINE:x64 /OUT:prog.exe res/res_myres.rc_myres.res prog.exe.p/prog.c.obj "/release" "/nologo" "/DEBUG" "/PDB:prog.pdb" "/SUBSYSTEM:WINDOWS"
prog.c.obj : error LNK2019: unresolved external symbol __imp_LoadIconA referenced in function WinMain

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants