Merge pull request #9375 from dcbaker/submit/windows-module-typing

Typing for the Windows module
pull/9496/head
Jussi Pakkanen 3 years ago committed by GitHub
commit af85738daf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      docs/markdown/Windows-module.md
  2. 22
      docs/markdown/snippets/windows_custom_targets.md
  3. 2
      mesonbuild/build.py
  4. 9
      mesonbuild/interpreter/type_checking.py
  5. 131
      mesonbuild/modules/windows.py
  6. 1
      run_mypy.py

@ -7,10 +7,22 @@ Windows.
### compile_resources
```
windows.compile_resources(...(string | File | CustomTarget | CustomTargetIndex),
args: []string,
depend_files: [](string | File),
depends: [](BuildTarget | CustomTarget)
include_directories: [](IncludeDirectories | string)): []CustomTarget
```
Compiles Windows `rc` files specified in the positional arguments.
Returns an opaque object that you put in the list of sources for the
target you want to have the resources in. This method has the
following keyword argument.
Returns a list of `CustomTarget` objects that you put in the list of sources for
the target you want to have the resources in.
*Since 0.61.0* CustomTargetIndexs and CustomTargets with more than out output
*may be used as positional arguments.
This method has the following keyword arguments:
- `args` lists extra arguments to pass to the resource compiler
- `depend_files` lists resource files that the resource script depends on

@ -0,0 +1,22 @@
## Windows.compile_resources CustomTarget
Previously the Windows module only accepted CustomTargets with one output, it
now accepts them with more than one output, and creates a windows resource
target for each output. Additionally it now accepts indexes of CustomTargets
```meson
ct = custom_target(
'multiple',
output : ['resource', 'another resource'],
...
)
ct2 = custom_target(
'slice',
output : ['resource', 'not a resource'],
...
)
resources = windows.compile_resources(ct, ct2[0])
```

@ -2700,7 +2700,7 @@ class CustomTargetIndex(HoldableObject):
def get_filename(self) -> str:
return self.output
def get_id(self):
def get_id(self) -> str:
return self.target.get_id()
def get_all_link_deps(self):

@ -6,7 +6,7 @@
import typing as T
from .. import compilers
from ..build import EnvironmentVariables, CustomTarget, BuildTarget, CustomTargetIndex, ExtractedObjects, GeneratedList
from ..build import EnvironmentVariables, CustomTarget, BuildTarget, CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs
from ..coredata import UserFeatureOption
from ..interpreterbase import TYPE_var
from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo
@ -278,3 +278,10 @@ CT_INSTALL_DIR_KW: KwargInfo[T.List[T.Union[str, bool]]] = KwargInfo(
)
CT_BUILD_BY_DEFAULT: KwargInfo[T.Optional[bool]] = KwargInfo('build_by_default', (bool, type(None)), since='0.40.0')
INCLUDE_DIRECTORIES: KwargInfo[T.List[T.Union[str, IncludeDirs]]] = KwargInfo(
'include_dirs',
ContainerTypeInfo(list, (str, IncludeDirs)),
listify=True,
default=[],
)

@ -15,40 +15,58 @@
import enum
import os
import re
import typing as T
from .. import mlog
from .. import mesonlib, build
from ..mesonlib import MachineChoice, MesonException, extract_as_list
from . import ModuleReturnValue
from . import ExtensionModule
from ..interpreterbase import permittedKwargs, FeatureNewKwargs, flatten
from . import ModuleReturnValue
from .. import mesonlib, build
from .. import mlog
from ..interpreter.type_checking import DEPEND_FILES_KW, DEPENDS_KW, INCLUDE_DIRECTORIES
from ..interpreterbase.decorators import ContainerTypeInfo, FeatureNew, KwargInfo, typed_kwargs, typed_pos_args
from ..mesonlib import MachineChoice, MesonException
from ..programs import ExternalProgram
if T.TYPE_CHECKING:
from . import ModuleState
from ..compilers import Compiler
from ..interpreter import Interpreter
from typing_extensions import TypedDict
class CompileResources(TypedDict):
depend_files: T.List[mesonlib.FileOrString]
depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]]
include_directories: T.List[T.Union[str, build.IncludeDirs]]
args: T.List[str]
class ResourceCompilerType(enum.Enum):
windres = 1
rc = 2
wrc = 3
class WindowsModule(ExtensionModule):
def __init__(self, interpreter):
def __init__(self, interpreter: 'Interpreter'):
super().__init__(interpreter)
self._rescomp: T.Optional[T.Tuple[ExternalProgram, ResourceCompilerType]] = None
self.methods.update({
'compile_resources': self.compile_resources,
})
def detect_compiler(self, compilers):
def detect_compiler(self, compilers: T.Dict[str, 'Compiler']) -> 'Compiler':
for l in ('c', 'cpp'):
if l in compilers:
return compilers[l]
raise MesonException('Resource compilation requires a C or C++ compiler.')
def _find_resource_compiler(self, state):
def _find_resource_compiler(self, state: 'ModuleState') -> T.Tuple[ExternalProgram, ResourceCompilerType]:
# FIXME: Does not handle `native: true` executables, see
# See https://github.com/mesonbuild/meson/issues/1531
# Take a parameter instead of the hardcoded definition below
for_machine = MachineChoice.HOST
if hasattr(self, '_rescomp'):
if self._rescomp:
return self._rescomp
# Will try cross / native file and then env var
@ -80,22 +98,26 @@ class WindowsModule(ExtensionModule):
return self._rescomp
@FeatureNewKwargs('windows.compile_resources', '0.47.0', ['depend_files', 'depends'])
@permittedKwargs({'args', 'include_directories', 'depend_files', 'depends'})
def compile_resources(self, state, args, kwargs):
extra_args = mesonlib.stringlistify(flatten(kwargs.get('args', [])))
wrc_depend_files = extract_as_list(kwargs, 'depend_files', pop = True)
wrc_depends = extract_as_list(kwargs, 'depends', pop = True)
@typed_pos_args('windows.compile_resources', varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex), min_varargs=1)
@typed_kwargs(
'winddows.compile_resoures',
DEPEND_FILES_KW.evolve(since='0.47.0'),
DEPENDS_KW.evolve(since='0.47.0'),
INCLUDE_DIRECTORIES.evolve(name='include_directories'),
KwargInfo('args', ContainerTypeInfo(list, str), default=[], listify=True),
)
def compile_resources(self, state: 'ModuleState',
args: T.Tuple[T.List[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex]]],
kwargs: 'CompileResources') -> ModuleReturnValue:
extra_args = kwargs['args'].copy()
wrc_depend_files = kwargs['depend_files']
wrc_depends = kwargs['depends']
for d in wrc_depends:
if isinstance(d, build.CustomTarget):
extra_args += state.get_include_args([
build.IncludeDirs('', [], False, [os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(d))])
])
inc_dirs = extract_as_list(kwargs, 'include_directories', pop = True)
for incd in inc_dirs:
if not isinstance(incd, (str, build.IncludeDirs)):
raise MesonException('Resource include dirs should be include_directories().')
extra_args += state.get_include_args(inc_dirs)
extra_args += state.get_include_args(kwargs['include_directories'])
rescomp, rescomp_type = self._find_resource_compiler(state)
if rescomp_type == ResourceCompilerType.rc:
@ -119,53 +141,56 @@ class WindowsModule(ExtensionModule):
suffix = 'o'
res_args = extra_args + ['@INPUT@', '-o', '@OUTPUT@']
res_targets = []
def add_target(src):
if isinstance(src, list):
for subsrc in src:
add_target(subsrc)
return
if isinstance(src, str):
name_formatted = src
name = os.path.join(state.subdir, src)
elif isinstance(src, mesonlib.File):
name_formatted = src.fname
name = src.relative_name()
elif isinstance(src, build.CustomTarget):
if len(src.get_outputs()) > 1:
raise MesonException('windows.compile_resources does not accept custom targets with more than 1 output.')
# Chances are that src.get_filename() is already the name of that
# target, add a prefix to avoid name clash.
name_formatted = 'windows_compile_resources_' + src.get_filename()
name = src.get_id()
else:
raise MesonException(f'Unexpected source type {src!r}. windows.compile_resources accepts only strings, files, custom targets, and lists thereof.')
res_targets: T.List[build.CustomTarget] = []
def get_names() -> T.Iterable[T.Tuple[str, str, T.Union[str, mesonlib.File, build.CustomTargetIndex]]]:
for src in args[0]:
if isinstance(src, str):
yield os.path.join(state.subdir, src), src, src
elif isinstance(src, mesonlib.File):
yield src.relative_name(), src.fname, src
elif isinstance(src, build.CustomTargetIndex):
FeatureNew.single_use('windows.compile_resource CustomTargetIndex in positional arguments', '0.61.0', state.subproject)
# This dance avoids a case where two indexs of the same
# target are given as separate arguments.
yield (f'{src.get_id()}_{src.target.get_outputs().index(src.output)}',
f'windows_compile_resources_{src.get_filename()}', src)
else:
if len(src.get_outputs()) > 1:
FeatureNew.single_use('windows.compile_resource CustomTarget with multiple outputs in positional arguments', '0.61.0', state.subproject)
for i, out in enumerate(src.get_outputs()):
# Chances are that src.get_filename() is already the name of that
# target, add a prefix to avoid name clash.
yield f'{src.get_id()}_{i}', f'windows_compile_resources_{i}_{out}', src[i]
for name, name_formatted, src in get_names():
# Path separators are not allowed in target names
name = name.replace('/', '_').replace('\\', '_').replace(':', '_')
name_formatted = name_formatted.replace('/', '_').replace('\\', '_').replace(':', '_')
output = f'{name}_@BASENAME@.{suffix}'
command: T.List[T.Union[str, ExternalProgram]] = []
command.append(rescomp)
command.extend(res_args)
res_kwargs = {
'output': name + '_@BASENAME@.' + suffix,
'output': output,
'input': [src],
'command': [rescomp] + res_args,
'depend_files': wrc_depend_files,
'depends': wrc_depends,
}
# instruct binutils windres to generate a preprocessor depfile
if rescomp_type == ResourceCompilerType.windres:
res_kwargs['depfile'] = res_kwargs['output'] + '.d'
res_kwargs['command'] += ['--preprocessor-arg=-MD', '--preprocessor-arg=-MQ@OUTPUT@', '--preprocessor-arg=-MF@DEPFILE@']
res_kwargs['depfile'] = f'{output}.d'
command.extend(['--preprocessor-arg=-MD',
'--preprocessor-arg=-MQ@OUTPUT@',
'--preprocessor-arg=-MF@DEPFILE@'])
res_targets.append(build.CustomTarget(name_formatted, state.subdir, state.subproject, res_kwargs))
res_kwargs['command'] = command
add_target(args)
res_targets.append(build.CustomTarget(name_formatted, state.subdir, state.subproject, res_kwargs))
return ModuleReturnValue(res_targets, [res_targets])
def initialize(*args, **kwargs):
return WindowsModule(*args, **kwargs)
def initialize(interp: 'Interpreter') -> WindowsModule:
return WindowsModule(interp)

@ -44,6 +44,7 @@ modules = [
'mesonbuild/modules/qt.py',
'mesonbuild/modules/unstable_external_project.py',
'mesonbuild/modules/unstable_rust.py',
'mesonbuild/modules/windows.py',
'mesonbuild/mparser.py',
'mesonbuild/msetup.py',
'mesonbuild/mtest.py',

Loading…
Cancel
Save