diff --git a/mesonbuild/modules/unstable_icestorm.py b/mesonbuild/modules/unstable_icestorm.py index 0f6502dac..b4ddd5c53 100644 --- a/mesonbuild/modules/unstable_icestorm.py +++ b/mesonbuild/modules/unstable_icestorm.py @@ -13,83 +13,119 @@ # limitations under the License. from __future__ import annotations - +import itertools import typing as T -from . import ExtensionModule +from . import ExtensionModule, ModuleReturnValue +from .. import build from .. import mesonlib +from ..interpreter.type_checking import CT_INPUT_KW from ..interpreterbase import FeatureNew -from ..interpreterbase import flatten +from ..interpreterbase.decorators import KwargInfo, typed_kwargs, typed_pos_args if T.TYPE_CHECKING: + from typing_extensions import TypedDict + + from . import ModuleState + from ..interpreter import Interpreter from ..programs import ExternalProgram + class ProjectKwargs(TypedDict): + + sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]] + constraint_file: T.Union[mesonlib.FileOrString, build.GeneratedTypes] + class IceStormModule(ExtensionModule): @FeatureNew('FPGA/Icestorm Module', '0.45.0') - def __init__(self, interpreter): + def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) self.tools: T.Dict[str, ExternalProgram] = {} self.methods.update({ 'project': self.project, }) - def detect_tools(self, state): + def detect_tools(self, state: ModuleState) -> None: self.tools['yosys'] = state.find_program('yosys') self.tools['arachne'] = state.find_program('arachne-pnr') self.tools['icepack'] = state.find_program('icepack') self.tools['iceprog'] = state.find_program('iceprog') self.tools['icetime'] = state.find_program('icetime') - def project(self, state, args, kwargs): + @typed_pos_args('icestorm.project', str, + varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, + build.GeneratedList)) + @typed_kwargs( + 'icestorm.project', + CT_INPUT_KW.evolve(name='sources'), + KwargInfo( + 'constraint_file', + (str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList), + required=True, + ) + ) + def project(self, state: ModuleState, + args: T.Tuple[str, T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]], + kwargs: ProjectKwargs) -> ModuleReturnValue: if not self.tools: self.detect_tools(state) - if not args: - raise mesonlib.MesonException('Project requires at least one argument, which is the project name.') - proj_name = args[0] - arg_sources = args[1:] - if not isinstance(proj_name, str): - raise mesonlib.MesonException('Argument must be a string.') - kwarg_sources = kwargs.get('sources', []) - if not isinstance(kwarg_sources, list): - kwarg_sources = [kwarg_sources] - all_sources = self.interpreter.source_strings_to_files(flatten(arg_sources + kwarg_sources)) - if 'constraint_file' not in kwargs: - raise mesonlib.MesonException('Constraint file not specified.') - - constraint_file = self.interpreter.source_strings_to_files(kwargs['constraint_file']) - if len(constraint_file) != 1: - raise mesonlib.MesonException('Constraint file must contain one and only one entry.') - blif_name = proj_name + '_blif' - blif_fname = proj_name + '.blif' - asc_name = proj_name + '_asc' - asc_fname = proj_name + '.asc' - bin_name = proj_name + '_bin' - bin_fname = proj_name + '.bin' - time_name = proj_name + '-time' - upload_name = proj_name + '-upload' - - blif_target = self.interpreter.func_custom_target(None, [blif_name], { - 'input': all_sources, - 'output': blif_fname, - 'command': [self.tools['yosys'], '-q', '-p', 'synth_ice40 -blif @OUTPUT@', '@INPUT@']}) - - asc_target = self.interpreter.func_custom_target(None, [asc_name], { - 'input': blif_target, - 'output': asc_fname, - 'command': [self.tools['arachne'], '-q', '-d', '1k', '-p', constraint_file, '@INPUT@', '-o', '@OUTPUT@']}) - - bin_target = self.interpreter.func_custom_target(None, [bin_name], { - 'input': asc_target, - 'output': bin_fname, - 'command': [self.tools['icepack'], '@INPUT@', '@OUTPUT@'], - 'build_by_default': True}) - - self.interpreter.func_run_target(None, [upload_name], { - 'command': [self.tools['iceprog'], bin_target]}) - - self.interpreter.func_run_target(None, [time_name], { - 'command': [self.tools['icetime'], bin_target]}) - -def initialize(*args, **kwargs): - return IceStormModule(*args, **kwargs) + proj_name, arg_sources = args + all_sources = self.interpreter.source_strings_to_files( + list(itertools.chain(arg_sources, kwargs['sources']))) + + blif_target = build.CustomTarget( + f'{proj_name}_blif', + state.subdir, + state.subproject, + state.environment, + [self.tools['yosys'], '-q', '-p', 'synth_ice40 -blif @OUTPUT@', '@INPUT@'], + all_sources, + [f'{proj_name}.blif'], + ) + + asc_target = build.CustomTarget( + f'{proj_name}_asc', + state.subdir, + state.subproject, + state.environment, + [self.tools['arachne'], '-q', '-d', '1k', '-p', '@INPUT@', '-o', '@OUTPUT@'], + [kwargs['constraint_file'], blif_target], + [f'{proj_name}.asc'], + ) + + bin_target = build.CustomTarget( + f'{proj_name}_bin', + state.subdir, + state.subproject, + state.environment, + [self.tools['icepack'], '@INPUT@', '@OUTPUT@'], + [asc_target], + [f'{proj_name}.bin'], + build_by_default=True, + ) + + upload_target = build.RunTarget( + f'{proj_name}-upload', + [self.tools['iceprog'], bin_target], + [], + state.subdir, + state.subproject, + state.environment, + ) + + time_target = build.RunTarget( + f'{proj_name}-time', + [self.tools['icetime'], bin_target], + [], + state.subdir, + state.subproject, + state.environment, + ) + + return ModuleReturnValue( + None, + [blif_target, asc_target, bin_target, upload_target, time_target]) + + +def initialize(interp: Interpreter) -> IceStormModule: + return IceStormModule(interp) diff --git a/run_mypy.py b/run_mypy.py index b7af49e83..12a12b973 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -49,6 +49,7 @@ modules = [ 'mesonbuild/modules/qt.py', 'mesonbuild/modules/sourceset.py', 'mesonbuild/modules/unstable_external_project.py', + 'mesonbuild/modules/unstable_icestorm.py', 'mesonbuild/modules/unstable_rust.py', 'mesonbuild/modules/windows.py', 'mesonbuild/mparser.py',