Merge pull request #9375 from dcbaker/submit/windows-module-typing
Typing for the Windows module
This commit is contained in:
commit
af85738daf
|
@ -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 = []
|
||||
res_targets: T.List[build.CustomTarget] = []
|
||||
|
||||
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.')
|
||||
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_kwargs['command'] = command
|
||||
|
||||
res_targets.append(build.CustomTarget(name_formatted, state.subdir, state.subproject, res_kwargs))
|
||||
|
||||
add_target(args)
|
||||
|
||||
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…
Reference in New Issue