diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 2239fdd9e..4e470cfa0 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -42,9 +42,6 @@ if T.TYPE_CHECKING: from ..interpreter import Interpreter, Test from ..mesonlib import FileMode - InstallType = T.List[T.Tuple[str, str, T.Optional['FileMode']]] - InstallSubdirsType = T.List[T.Tuple[str, str, T.Optional['FileMode'], T.Tuple[T.Set[str], T.Set[str]]]] - # Languages that can mix with C or C++ but don't support unity builds yet # because the syntax we use for unity builds is specific to C/++/ObjC/++. # Assembly files cannot be unitified and neither can LLVM IR files @@ -1451,7 +1448,12 @@ class Backend: for h in headers: outdir = h.get_custom_install_dir() if outdir is None: - outdir = os.path.join(incroot, h.get_install_subdir()) + subdir = h.get_install_subdir() + if subdir is None: + outdir = incroot + else: + outdir = os.path.join(incroot, subdir) + for f in h.get_sources(): if not isinstance(f, File): raise MesonException(f'Invalid header type {f!r} can\'t be installed') diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 707f5d2dc..f88bf740d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -135,7 +135,7 @@ class DependencyOverride(HoldableObject): class Headers(HoldableObject): def __init__(self, sources: T.List[File], install_subdir: T.Optional[str], - install_dir: T.Optional[str], install_mode: T.Optional['FileMode'], + install_dir: T.Optional[str], install_mode: 'FileMode', subproject: str): self.sources = sources self.install_subdir = install_subdir @@ -149,7 +149,7 @@ class Headers(HoldableObject): def set_install_subdir(self, subdir: str) -> None: self.install_subdir = subdir - def get_install_subdir(self) -> str: + def get_install_subdir(self) -> T.Optional[str]: return self.install_subdir def get_sources(self) -> T.List[File]: @@ -158,14 +158,14 @@ class Headers(HoldableObject): def get_custom_install_dir(self) -> T.Optional[str]: return self.custom_install_dir - def get_custom_install_mode(self) -> T.Optional['FileMode']: + def get_custom_install_mode(self) -> 'FileMode': return self.custom_install_mode class Man(HoldableObject): def __init__(self, sources: T.List[File], install_dir: T.Optional[str], - install_mode: T.Optional['FileMode'], subproject: str, + install_mode: 'FileMode', subproject: str, locale: T.Optional[str]): self.sources = sources self.custom_install_dir = install_dir @@ -176,7 +176,7 @@ class Man(HoldableObject): def get_custom_install_dir(self) -> T.Optional[str]: return self.custom_install_dir - def get_custom_install_mode(self) -> T.Optional['FileMode']: + def get_custom_install_mode(self) -> 'FileMode': return self.custom_install_mode def get_sources(self) -> T.List['File']: @@ -186,7 +186,7 @@ class Man(HoldableObject): class InstallDir(HoldableObject): def __init__(self, src_subdir: str, inst_subdir: str, install_dir: str, - install_mode: T.Optional['FileMode'], + install_mode: 'FileMode', exclude: T.Tuple[T.Set[str], T.Set[str]], strip_directory: bool, subproject: str, from_source_dir: bool = True): @@ -2623,7 +2623,7 @@ class ConfigurationData(HoldableObject): # during install. class Data(HoldableObject): def __init__(self, sources: T.List[File], install_dir: str, - install_mode: T.Optional['FileMode'], subproject: str, + install_mode: 'FileMode', subproject: str, rename: T.List[str] = None): self.sources = sources self.install_dir = install_dir diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2faec4e1e..f55e58a8b 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -85,6 +85,61 @@ def _language_validator(l: T.List[str]) -> T.Optional[str]: return None +def _install_mode_validator(mode: T.List[T.Union[str, bool, int]]) -> T.Optional[str]: + """Validate the `install_mode` keyword argument. + + This is a rather odd thing, it's a scalar, or an array of 3 values in the form: + [(str | False), (str | int | False) = False, (str | int | False) = False] + Where the second and third arguments are not required, and are considered to + default to False. + """ + if not mode: + return None + if True in mode: + return 'can only be a string or false, not true' + if len(mode) > 3: + return 'may have at most 3 elements' + + perms = mode[0] + if not isinstance(perms, (str, bool)): + return 'permissions part must be a string or false' + + if isinstance(perms, str): + if not len(perms) == 9: + return (f'permissions string must be exactly 9 characters, got "{len(perms)}" ' + 'in the form rwxr-xr-x') + for i in [0, 3, 6]: + if perms[i] not in {'-', 'r'}: + return f'bit {i} must be "-" or "r", not {perms[i]}' + for i in [1, 4, 7]: + if perms[i] not in {'-', 'w'}: + return f'bit {i} must be "-" or "w", not {perms[i]}' + for i in [2, 5]: + if perms[i] not in {'-', 'x', 's', 'S'}: + return f'bit {i} must be "-", "s", "S", or "x", not {perms[i]}' + if perms[8] not in {'-', 'x', 't', 'T'}: + return f'bit 8 must be "-", "t", "T", or "x", not {perms[8]}' + + if len(mode) >= 2 and not isinstance(mode[1], (int, str, bool)): + return 'second componenent must be a string, number, or False if provided' + if len(mode) >= 3 and not isinstance(mode[2], (int, str, bool)): + return 'third componenent must be a string, number, or False if provided' + + return None + + +def _install_mode_convertor(mode: T.Optional[T.List[T.Union[str, bool, int]]]) -> FileMode: + """Convert the DSL form of the `install_mode` keyword arugment to `FileMode` + + This is not required, and if not required returns None + + TODO: It's not clear to me why this needs to be None and not just return an + emtpy FileMode. + """ + # this has already been validated by the validator + return FileMode(*[m if isinstance(m, str) else None for m in mode]) + + _NATIVE_KW = KwargInfo( 'native', bool, default=False, @@ -97,6 +152,15 @@ _LANGUAGE_KW = KwargInfo( validator=_language_validator, convertor=lambda x: [i.lower() for i in x]) +_INSTALL_MODE_KW = KwargInfo( + 'install_mode', + ContainerTypeInfo(list, (str, bool, int)), + listify=True, + default=[], + validator=_install_mode_validator, + convertor=_install_mode_convertor, +) + def stringifyUserArguments(args, quote=False): if isinstance(args, list): @@ -1817,46 +1881,50 @@ This will become a hard error in the future.''' % kwargs['input'], location=self self.build.benchmarks.append(t) mlog.debug('Adding benchmark', mlog.bold(t.name, True)) - @FeatureNewKwargs('install_headers', '0.47.0', ['install_mode']) - @permittedKwargs({'install_dir', 'install_mode', 'subdir'}) - def func_install_headers(self, node, args, kwargs): - source_files = self.source_strings_to_files(args) - install_mode = self._get_kwarg_install_mode(kwargs) - - install_subdir = kwargs.get('subdir', '') - if not isinstance(install_subdir, str): - raise InterpreterException('subdir keyword argument must be a string') - elif os.path.isabs(install_subdir): + @typed_pos_args('install_headers', varargs=(str, mesonlib.File), min_varargs=1) + @typed_kwargs( + 'install_headers', + KwargInfo('install_dir', (str, None)), + KwargInfo('subdir', (str, None)), + _INSTALL_MODE_KW.evolve(since='0.47.0'), + ) + def func_install_headers(self, node: mparser.BaseNode, + args: T.Tuple[T.List['mesonlib.FileOrString']], + kwargs: 'kwargs.FuncInstallHeaders') -> build.Headers: + source_files = self.source_strings_to_files(args[0]) + install_subdir = kwargs['subdir'] + if install_subdir is not None and os.path.isabs(install_subdir): mlog.deprecation('Subdir keyword must not be an absolute path. This will be a hard error in the next release.') - install_dir = kwargs.get('install_dir', None) - if install_dir is not None and not isinstance(install_dir, str): - raise InterpreterException('install_dir keyword argument must be a string if provided') - - h = build.Headers(source_files, install_subdir, install_dir, install_mode, self.subproject) + h = build.Headers(source_files, install_subdir, kwargs['install_dir'], + kwargs['install_mode'], self.subproject) self.build.headers.append(h) return h - @FeatureNewKwargs('install_man', '0.47.0', ['install_mode']) - @FeatureNewKwargs('install_man', '0.58.0', ['locale']) - @permittedKwargs({'install_dir', 'install_mode', 'locale'}) - def func_install_man(self, node, args, kwargs): - sources = self.source_strings_to_files(args) + @typed_pos_args('install_man', varargs=(str, mesonlib.File), min_varargs=1) + @typed_kwargs( + 'install_man', + KwargInfo('install_dir', (str, None)), + KwargInfo('locale', (str, None), since='0.58.0'), + _INSTALL_MODE_KW.evolve(since='0.47.0') + ) + def func_install_man(self, node: mparser.BaseNode, + args: T.Tuple[T.List['mesonlib.FileOrString']], + kwargs: 'kwargs.FuncInstallMan') -> build.Man: + # We just need to narrow this, because the input is limited to files and + # Strings as inputs, so only Files will be returned + sources = self.source_strings_to_files(args[0]) for s in sources: try: - num = int(s.split('.')[-1]) + num = int(s.rsplit('.', 1)[-1]) except (IndexError, ValueError): num = 0 - if num < 1 or num > 8: - raise InvalidArguments('Man file must have a file extension of a number between 1 and 8') - custom_install_mode = self._get_kwarg_install_mode(kwargs) - custom_install_dir = kwargs.get('install_dir', None) - locale = kwargs.get('locale') - if custom_install_dir is not None and not isinstance(custom_install_dir, str): - raise InterpreterException('install_dir must be a string.') - - m = build.Man(sources, custom_install_dir, custom_install_mode, self.subproject, locale) + if not 1 <= num <= 9: + raise InvalidArguments('Man file must have a file extension of a number between 1 and 9') + + m = build.Man(sources, kwargs['install_dir'], kwargs['install_mode'], + self.subproject, kwargs['locale']) self.build.man.append(m) return m @@ -1929,82 +1997,55 @@ This will become a hard error in the future.''' % kwargs['input'], location=self 'permissions arg to be a string or false') return FileMode(*install_mode) - @FeatureNewKwargs('install_data', '0.46.0', ['rename']) - @FeatureNewKwargs('install_data', '0.38.0', ['install_mode']) - @permittedKwargs({'install_dir', 'install_mode', 'rename', 'sources'}) - def func_install_data(self, node, args: T.List, kwargs: T.Dict[str, T.Any]): - kwsource = mesonlib.stringlistify(kwargs.get('sources', [])) - raw_sources = args + kwsource - sources: T.List[mesonlib.File] = [] - source_strings: T.List[str] = [] - for s in raw_sources: - if isinstance(s, mesonlib.File): - sources.append(s) - elif isinstance(s, str): - source_strings.append(s) - else: - raise InvalidArguments('Argument must be string or file.') - sources += self.source_strings_to_files(source_strings) - install_dir: T.Optional[str] = kwargs.get('install_dir', None) - if install_dir is not None and not isinstance(install_dir, str): - raise InvalidArguments('Keyword argument install_dir not a string.') - install_mode = self._get_kwarg_install_mode(kwargs) - rename: T.Optional[T.List[str]] = kwargs.get('rename', None) - if rename is not None: - rename = mesonlib.stringlistify(rename) + @typed_pos_args('install_data', varargs=(str, mesonlib.File)) + @typed_kwargs( + 'install_data', + KwargInfo('install_dir', str), + KwargInfo('sources', ContainerTypeInfo(list, (str, mesonlib.File)), listify=True, default=[]), + KwargInfo('rename', ContainerTypeInfo(list, str), default=[], listify=True, since='0.46.0'), + _INSTALL_MODE_KW.evolve(since='0.38.0'), + ) + def func_install_data(self, node: mparser.BaseNode, + args: T.Tuple[T.List['mesonlib.FileOrString']], + kwargs: 'kwargs.FuncInstallData') -> build.Data: + sources = self.source_strings_to_files(args[0] + kwargs['sources']) + rename = kwargs['rename'] or None + if rename: if len(rename) != len(sources): raise InvalidArguments( '"rename" and "sources" argument lists must be the same length if "rename" is given. ' f'Rename has {len(rename)} elements and sources has {len(sources)}.') - data = build.Data(sources, install_dir, install_mode, self.subproject, rename) + data = build.Data( + sources, kwargs['install_dir'], kwargs['install_mode'], + self.subproject, rename) self.build.data.append(data) return data - @FeatureNewKwargs('install_subdir', '0.42.0', ['exclude_files', 'exclude_directories']) - @FeatureNewKwargs('install_subdir', '0.38.0', ['install_mode']) - @permittedKwargs({'exclude_files', 'exclude_directories', 'install_dir', 'install_mode', 'strip_directory'}) - @stringArgs - def func_install_subdir(self, node, args, kwargs): - if len(args) != 1: - raise InvalidArguments('Install_subdir requires exactly one argument.') - subdir: str = args[0] - if not isinstance(subdir, str): - raise InvalidArguments('install_subdir positional argument 1 must be a string.') - if 'install_dir' not in kwargs: - raise InvalidArguments('Missing keyword argument install_dir') - install_dir: str = kwargs['install_dir'] - if not isinstance(install_dir, str): - raise InvalidArguments('Keyword argument install_dir not a string.') - if 'strip_directory' in kwargs: - strip_directory: bool = kwargs['strip_directory'] - if not isinstance(strip_directory, bool): - raise InterpreterException('"strip_directory" keyword must be a boolean.') - else: - strip_directory = False - if 'exclude_files' in kwargs: - exclude: T.List[str] = extract_as_list(kwargs, 'exclude_files') - for f in exclude: - if not isinstance(f, str): - raise InvalidArguments('Exclude argument not a string.') - elif os.path.isabs(f): - raise InvalidArguments('Exclude argument cannot be absolute.') - exclude_files: T.Set[str] = set(exclude) - else: - exclude_files = set() - if 'exclude_directories' in kwargs: - exclude: T.List[str] = extract_as_list(kwargs, 'exclude_directories') - for d in exclude: - if not isinstance(d, str): - raise InvalidArguments('Exclude argument not a string.') - elif os.path.isabs(d): - raise InvalidArguments('Exclude argument cannot be absolute.') - exclude_directories: T.Set[str] = set(exclude) - else: - exclude_directories = set() - exclude = (exclude_files, exclude_directories) - install_mode = self._get_kwarg_install_mode(kwargs) - idir = build.InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory, self.subproject) + @typed_pos_args('install_subdir', str) + @typed_kwargs( + 'install_subdir', + KwargInfo('install_dir', str, required=True), + KwargInfo('strip_directory', bool, default=False), + KwargInfo('exclude_files', ContainerTypeInfo(list, str), + default=[], listify=True, since='0.42.0', + validator=lambda x: 'cannot be absolute' if any(os.path.isabs(d) for d in x) else None), + KwargInfo('exclude_directories', ContainerTypeInfo(list, str), + default=[], listify=True, since='0.42.0', + validator=lambda x: 'cannot be absolute' if any(os.path.isabs(d) for d in x) else None), + _INSTALL_MODE_KW.evolve(since='0.38.0'), + ) + def func_install_subdir(self, node: mparser.BaseNode, args: T.Tuple[str], + kwargs: 'kwargs.FuncInstallSubdir') -> build.InstallDir: + exclude = (set(kwargs['exclude_files']), set(kwargs['exclude_directories'])) + idir = build.InstallDir( + self.subdir, + args[0], + kwargs['install_dir'], + kwargs['install_mode'], + exclude, + kwargs['strip_directory'], + self.subproject) self.build.install_dirs.append(idir) return idir @@ -2479,6 +2520,10 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey if project_root / self.subproject_dir in norm.parents: raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} from a nested subproject.') + + @T.overload + def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString']) -> T.List['mesonlib.File']: ... + def source_strings_to_files(self, sources: T.List['SourceInputs']) -> T.List['SourceOutputs']: """Lower inputs to a list of Targets and Files, replacing any strings. diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 1cc208213..3c3ecf641 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -4,13 +4,13 @@ """Keyword Argument type annotations.""" -from mesonbuild import coredata import typing as T from typing_extensions import TypedDict, Literal from .. import build -from ..mesonlib import MachineChoice, File +from .. import coredata +from ..mesonlib import MachineChoice, File, FileMode, FileOrString from .interpreterobjects import EnvironmentVariablesObject @@ -102,3 +102,33 @@ class DependencyMethodPartialDependency(TypedDict): class BuildTargeMethodExtractAllObjects(TypedDict): recursive: bool + +class FuncInstallSubdir(TypedDict): + + install_dir: str + strip_directory: bool + exclude_files: T.List[str] + exclude_directories: T.List[str] + install_mode: FileMode + + +class FuncInstallData(TypedDict): + + install_dir: str + sources: T.List[FileOrString] + rename: T.List[str] + install_mode: FileMode + + +class FuncInstallHeaders(TypedDict): + + install_dir: T.Optional[str] + install_mode: FileMode + subdir: T.Optional[str] + + +class FuncInstallMan(TypedDict): + + install_dir: T.Optional[str] + install_mode: FileMode + locale: T.Optional[str] diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index a011b6681..b05df5bcd 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -272,6 +272,11 @@ class ContainerTypeInfo: _T = T.TypeVar('_T') +class _NULL_T: + """Special null type for evolution, this is an implementation detail.""" + + +_NULL = _NULL_T() class KwargInfo(T.Generic[_T]): @@ -329,6 +334,42 @@ class KwargInfo(T.Generic[_T]): self.convertor = convertor self.not_set_warning = not_set_warning + def evolve(self, *, + required: T.Union[bool, _NULL_T] = _NULL, + listify: T.Union[bool, _NULL_T] = _NULL, + default: T.Union[_T, None, _NULL_T] = _NULL, + since: T.Union[str, None, _NULL_T] = _NULL, + since_values: T.Union[T.Dict[str, str], None, _NULL_T] = _NULL, + deprecated: T.Union[str, None, _NULL_T] = _NULL, + deprecated_values: T.Union[T.Dict[str, str], None, _NULL_T] = _NULL, + validator: T.Union[T.Callable[[_T], T.Optional[str]], None, _NULL_T] = _NULL, + convertor: T.Union[T.Callable[[_T], TYPE_var], None, _NULL_T] = _NULL) -> 'KwargInfo': + """Create a shallow copy of this KwargInfo, with modifications. + + This allows us to create a new copy of a KwargInfo with modifications. + This allows us to use a shared kwarg that implements complex logic, but + has slight differences in usage, such as being added to different + functions in different versions of Meson. + + The use the _NULL special value here allows us to pass None, which has + meaning in many of these cases. _NULL itself is never stored, always + being replaced by either the copy in self, or the provided new version. + """ + return type(self)( + self.name, + self.types, + listify=listify if not isinstance(listify, _NULL_T) else self.listify, + required=required if not isinstance(required, _NULL_T) else self.required, + default=default if not isinstance(default, _NULL_T) else self.default, + since=since if not isinstance(since, _NULL_T) else self.since, + since_values=since_values if not isinstance(since_values, _NULL_T) else self.since_values, + deprecated=deprecated if not isinstance(deprecated, _NULL_T) else self.deprecated, + deprecated_values=deprecated_values if not isinstance(deprecated_values, _NULL_T) else self.deprecated_values, + validator=validator if not isinstance(validator, _NULL_T) else self.validator, + convertor=convertor if not isinstance(convertor, _NULL_T) else self.convertor, + ) + + def typed_kwargs(name: str, *types: KwargInfo) -> T.Callable[..., T.Any]: """Decorator for type checking keyword arguments. diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index 3714ecdea..160cc377d 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -298,8 +298,8 @@ class FileMode: '[r-][w-][xsS-]' # Group perms '[r-][w-][xtT-]') # Others perms - def __init__(self, perms: T.Optional[str] = None, owner: T.Optional[str] = None, - group: T.Optional[str] = None): + def __init__(self, perms: T.Optional[str] = None, owner: T.Union[str, int, None] = None, + group: T.Union[str, int, None] = None): self.perms_s = perms self.perms = self.perms_s_to_bits(perms) self.owner = owner @@ -422,8 +422,11 @@ class File(HoldableObject): def endswith(self, ending: str) -> bool: return self.fname.endswith(ending) - def split(self, s: str) -> T.List[str]: - return self.fname.split(s) + def split(self, s: str, maxsplit: int = -1) -> T.List[str]: + return self.fname.split(s, maxsplit=maxsplit) + + def rsplit(self, s: str, maxsplit: int = -1) -> T.List[str]: + return self.fname.rsplit(s, maxsplit=maxsplit) def __eq__(self, other: object) -> bool: if not isinstance(other, File): diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index e14a04704..7011c42f1 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -64,7 +64,7 @@ build definitions so that it will not break when the change happens.''' selinux_updates: T.List[str] = [] -def add_arguments(parser: argparse.Namespace) -> None: +def add_arguments(parser: argparse.ArgumentParser) -> None: parser.add_argument('-C', default='.', dest='wd', help='directory to cd into before running') parser.add_argument('--profile-self', action='store_true', dest='profile', @@ -130,7 +130,8 @@ def append_to_log(lf: T.TextIO, line: str) -> None: lf.flush() -def set_chown(path: str, user: T.Optional[str] = None, group: T.Optional[str] = None, +def set_chown(path: str, user: T.Union[str, int, None] = None, + group: T.Union[str, int, None] = None, dir_fd: T.Optional[int] = None, follow_symlinks: bool = True) -> None: # shutil.chown will call os.chown without passing all the parameters # and particularly follow_symlinks, thus we replace it temporary @@ -182,12 +183,12 @@ def sanitize_permissions(path: str, umask: T.Union[str, int]) -> None: def set_mode(path: str, mode: T.Optional['FileMode'], default_umask: T.Union[str, int]) -> None: - if mode is None or (mode.perms_s or mode.owner or mode.group) is None: + if mode is None or all(m is None for m in [mode.perms_s, mode.owner, mode.group]): # Just sanitize permissions with the default umask sanitize_permissions(path, default_umask) return # No chown() on Windows, and must set one of owner/group - if not is_windows() and (mode.owner or mode.group) is not None: + if not is_windows() and (mode.owner is not None or mode.group is not None): try: set_chown(path, mode.owner, mode.group, follow_symlinks=False) except PermissionError as e: diff --git a/run_unittests.py b/run_unittests.py index 13149ce89..7f74e91db 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1698,6 +1698,18 @@ class InternalTests(unittest.TestCase): _(None, mock.Mock(subproject=''), [], {'mode': 'since'}) self.assertRegex(out.getvalue(), r"""WARNING:.Project targeting '1.0'.*introduced in '1.1': "testfunc" keyword argument "mode" value "since".*""") + def test_typed_kwarg_evolve(self) -> None: + k = KwargInfo('foo', str, required=True, default='foo') + v = k.evolve(default='bar') + self.assertEqual(k.name, 'foo') + self.assertEqual(k.name, v.name) + self.assertEqual(k.types, str) + self.assertEqual(k.types, v.types) + self.assertEqual(k.required, True) + self.assertEqual(k.required, v.required) + self.assertEqual(k.default, 'foo') + self.assertEqual(v.default, 'bar') + @unittest.skipIf(is_tarball(), 'Skipping because this is a tarball release') class DataTests(unittest.TestCase): diff --git a/test cases/failing/30 invalid man extension/test.json b/test cases/failing/30 invalid man extension/test.json index 3f77a04f4..3e5f45de5 100644 --- a/test cases/failing/30 invalid man extension/test.json +++ b/test cases/failing/30 invalid man extension/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/30 invalid man extension/meson.build:2:0: ERROR: Man file must have a file extension of a number between 1 and 8" + "line": "test cases/failing/30 invalid man extension/meson.build:2:0: ERROR: Man file must have a file extension of a number between 1 and 9" } ] } diff --git a/test cases/failing/31 no man extension/test.json b/test cases/failing/31 no man extension/test.json index 6e1f542c4..0972da1f3 100644 --- a/test cases/failing/31 no man extension/test.json +++ b/test cases/failing/31 no man extension/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/31 no man extension/meson.build:2:0: ERROR: Man file must have a file extension of a number between 1 and 8" + "line": "test cases/failing/31 no man extension/meson.build:2:0: ERROR: Man file must have a file extension of a number between 1 and 9" } ] } diff --git a/test cases/failing/95 custom target install data/test.json b/test cases/failing/95 custom target install data/test.json index 64ef53070..c8004d660 100644 --- a/test cases/failing/95 custom target install data/test.json +++ b/test cases/failing/95 custom target install data/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/95 custom target install data/meson.build:11:0: ERROR: Argument must be string or file." + "line": "test cases/failing/95 custom target install data/meson.build:11:0: ERROR: install_data argument 1 was of type \"CustomTarget\" but should have been one of: \"str\", \"File\"" } ] }