diff options
62 files changed, 802 insertions, 177 deletions
diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md index 3542aa4..f905d2c 100644 --- a/docs/markdown/Commands.md +++ b/docs/markdown/Commands.md @@ -150,6 +150,35 @@ Create a project in `sourcedir`: meson init -C sourcedir ``` +### env2mfile + +*This command is experimental and subject to change.* + +*{Since 0.62.0}* + +{{ env2mfile_usage.inc }} + +Create native and cross files from the current environment, typically +by sniffing environment variables like `CC` and `CFLAGS`. + +{{ env2mfile_arguments.inc }} + +#### Examples: + +Autodetect the current cross build environment: + +``` +meson env2mfile --cross -o current_cross.txt --cpu=arm7a --cpu-family=arm --system=linux +``` + +Generate a cross build using Debian system information: + +``` +meson env2mfile --cross --debarch=armhf -o deb_arm_cross.txt +``` + + + ### introspect {{ introspect_usage.inc }} diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 8165955..15da929 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -625,6 +625,12 @@ for OpenMP support. The `language` keyword may used. +## OpenSSL + +*(added 0.62.0)* + +`method` may be `auto`, `pkg-config`, `system` or `cmake`. + ## pcap *(added 0.42.0)* diff --git a/docs/markdown/snippets/dataonly-pkgconfig-default-install-path.md b/docs/markdown/snippets/dataonly-pkgconfig-default-install-path.md new file mode 100644 index 0000000..d968158 --- /dev/null +++ b/docs/markdown/snippets/dataonly-pkgconfig-default-install-path.md @@ -0,0 +1,4 @@ +## `dataonly` Pkgconfig Default Install Path + +The default install path for `dataonly` pkgconfig files has changed from +`${libdir}/pkgconfig` to `${datadir}/pkgconfig`. diff --git a/docs/markdown/snippets/env2mfile.md b/docs/markdown/snippets/env2mfile.md new file mode 100644 index 0000000..a575618 --- /dev/null +++ b/docs/markdown/snippets/env2mfile.md @@ -0,0 +1,40 @@ +## Experimental command to convert environments to cross files + +Meson has a new command `env2mfile` that can be used to convert +"environment variable based" cross and native compilation environments +to Meson machine files. This is especially convenient for e.g. distro +packagers so they can easily generate unambiguous configuration files +for packge building. + +As an example here's how you would generate a cross file that takes +its settings from the `CC`, `CXX`, `CFLAGS` etc environment variables. + + meson env2mfile --cross --system=baremetal --cpu=armv7 --cpu-family=arm -o armcross.txt + +The command also has support for generating Debian build files using +system introspection: + + meson env2mfile --cross --debarch armhf -o debarmhf_cross.txt + +Note how you don't need to specify any system details, the command +gets them transparently via `dpkg-architecture`. + +Creating a native file is done in the same way: + + meson env2mfile --native -o current_system.txt + +This system will detect if the `_FOR_BUILD` environment variables are +enabled and then uses them as needed. + +With this you should be able to convert any envvar-based cross build +setup to cross and native files and then use those. This means, among +other things, that you can then run your compilations from any shell, +not just the special one that has all the environment variables set. + +As this functionality is still a bit in flux, the specific behaviour +and command line arguments to use are subject to change. Because of +this the main documentation has not yet been updated. + +Please try this for your use cases and report to us if it is working. +Patches to make the autodetection work on other distros and platforms +are also welcome. diff --git a/docs/markdown/snippets/openssl_dependency.md b/docs/markdown/snippets/openssl_dependency.md new file mode 100644 index 0000000..f6f9b63 --- /dev/null +++ b/docs/markdown/snippets/openssl_dependency.md @@ -0,0 +1,11 @@ +## New custom dependency for OpenSSL + +Detecting an OpenSSL installation in a cross-platform manner can be +complicated. Officially, pkg-config is supported by upstream. Unofficially, +cmake includes a FindOpenSSL using a different name and which requires +specifying modules. + +Meson will now allow the pkg-config name to work in all cases using the following lookup order: +- prefer pkg-config if at all possible +- attempt to probe the system for the standard library naming, and retrieve the version from the headers +- if all else fails, check if cmake can find it diff --git a/docs/markdown/snippets/rust_proc_macro_crates.md b/docs/markdown/snippets/rust_proc_macro_crates.md new file mode 100644 index 0000000..780a5b3 --- /dev/null +++ b/docs/markdown/snippets/rust_proc_macro_crates.md @@ -0,0 +1,16 @@ +## Rust proc-macro crates + +Rust has these handy things called proc-macro crates, which are a bit like a +compiler plugin. We can now support them, simply build a [[shared_library]] with +the `rust_crate_type` set to `proc-macro`. + +```meson +proc = shared_library( + 'proc', + 'proc.rs', + rust_crate_type : 'proc-macro', + install : false, +) + +user = executable('user, 'user.rs', link_with : proc) +``` diff --git a/docs/markdown/snippets/strip.md b/docs/markdown/snippets/strip.md new file mode 100644 index 0000000..e971df6 --- /dev/null +++ b/docs/markdown/snippets/strip.md @@ -0,0 +1,5 @@ +## `meson install --strip` + +It is now possible to strip targets using `meson install --strip` even if +`-Dstrip=true` option was not set during configuration. This allows doing +stripped and not stripped installations without reconfiguring the build. diff --git a/docs/yaml/functions/_build_target_base.yaml b/docs/yaml/functions/_build_target_base.yaml index 4db37f4..87966f6 100644 --- a/docs/yaml/functions/_build_target_base.yaml +++ b/docs/yaml/functions/_build_target_base.yaml @@ -276,3 +276,23 @@ kwargs: version specification such as `windows,6.0`. See [MSDN documentation](https://docs.microsoft.com/en-us/cpp/build/reference/subsystem-specify-subsystem) for the full list. + + rust_crate_type: + type: str + since: 0.41.0 + description: | + Set the specific type of rust crate to compile (when compiling rust). + + If the target is an [[executable]] this defaults to "bin", the only + allowed value. + + If it is a [[static_library]] it defaults to "lib", and may be "lib", + "staticlib", or "rlib". If "lib" then Rustc will pick a default, "staticlib" + means a C ABI library, "rlib" means a Rust ABI. + + If it is a [[shared_library]] it defaults to "lib", and may be "lib", + "dylib", "cdylib", or "proc-macro". If "lib" then Rustc will pick a + default, "cdylib" means a C ABI library, "dylib" means a Rust ABI, and + "proc-macro" is a special rust proceedural macro crate. + + "proc-macro" is new in 0.62.0. diff --git a/man/meson.1 b/man/meson.1 index 0443d6e..8a7999e 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "January 2022" "meson 0.61.0" "User Commands" +.TH MESON "1" "March 2022" "meson 0.62.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index e5ff266..e3d3e93 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -31,7 +31,6 @@ from ..interpreterbase import ( ) from ..interpreter import ( - Interpreter, StringHolder, BooleanHolder, IntegerHolder, @@ -64,6 +63,9 @@ from ..mparser import ( import os, sys import typing as T +if T.TYPE_CHECKING: + from ..interpreter import Interpreter + class DontCareObject(MesonInterpreterObject): pass @@ -379,15 +381,15 @@ class AstInterpreter(InterpreterBase): mkwargs = {} # type: T.Dict[str, TYPE_nvar] try: if isinstance(src, str): - result = StringHolder(src, T.cast(Interpreter, self)).method_call(node.name, margs, mkwargs) + result = StringHolder(src, T.cast('Interpreter', self)).method_call(node.name, margs, mkwargs) elif isinstance(src, bool): - result = BooleanHolder(src, T.cast(Interpreter, self)).method_call(node.name, margs, mkwargs) + result = BooleanHolder(src, T.cast('Interpreter', self)).method_call(node.name, margs, mkwargs) elif isinstance(src, int): - result = IntegerHolder(src, T.cast(Interpreter, self)).method_call(node.name, margs, mkwargs) + result = IntegerHolder(src, T.cast('Interpreter', self)).method_call(node.name, margs, mkwargs) elif isinstance(src, list): - result = ArrayHolder(src, T.cast(Interpreter, self)).method_call(node.name, margs, mkwargs) + result = ArrayHolder(src, T.cast('Interpreter', self)).method_call(node.name, margs, mkwargs) elif isinstance(src, dict): - result = DictHolder(src, T.cast(Interpreter, self)).method_call(node.name, margs, mkwargs) + result = DictHolder(src, T.cast('Interpreter', self)).method_call(node.name, margs, mkwargs) except mesonlib.MesonException: return None diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b98eb08..6a78ed0 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -144,6 +144,7 @@ class TargetInstallData: subproject: str optional: bool = False tag: T.Optional[str] = None + can_strip: bool = False def __post_init__(self, outdir_name: str) -> None: self.out_name = os.path.join(outdir_name, os.path.basename(self.fname)) @@ -328,7 +329,7 @@ class Backend: # cast, we know what's in coredata anyway. # TODO: if it's possible to annotate get_option or validate_option_value # in the future we might be able to remove the cast here - return T.cast(T.Union[str, int, bool, 'WrapMode'], v) + return T.cast('T.Union[str, int, bool, WrapMode]', v) def get_source_dir_include_args(self, target: build.BuildTarget, compiler: 'Compiler', *, absolute_path: bool = False) -> T.List[str]: curdir = target.get_subdir() @@ -937,7 +938,7 @@ class Backend: commands += compiler.get_no_warn_args() else: # warning_level is a string, but mypy can't determine that - commands += compiler.get_warn_args(T.cast(str, self.get_option_for_target(OptionKey('warning_level'), target))) + commands += compiler.get_warn_args(T.cast('str', self.get_option_for_target(OptionKey('warning_level'), target))) # Add -Werror if werror=true is set in the build options set on the # command-line or default_options inside project(). This only sets the # action to be done for warnings if/when they are emitted, so it's ok @@ -1573,7 +1574,8 @@ class Backend: # # TODO: Create GNUStrip/AppleStrip/etc. hierarchy for more # fine-grained stripping of static archives. - should_strip = not isinstance(t, build.StaticLibrary) and self.get_option_for_target(OptionKey('strip'), t) + can_strip = not isinstance(t, build.StaticLibrary) + should_strip = can_strip and self.get_option_for_target(OptionKey('strip'), t) assert isinstance(should_strip, bool), 'for mypy' # Install primary build output (library/executable/jar, etc) # Done separately because of strip/aliases/rpath @@ -1584,7 +1586,7 @@ class Backend: install_dir_name, should_strip, mappings, t.rpath_dirs_to_remove, t.install_rpath, install_mode, t.subproject, - tag=tag) + tag=tag, can_strip=can_strip) d.targets.append(i) for alias, to, tag in t.get_aliases(): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f2de50c..fbc1618 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -712,7 +712,7 @@ class Target(HoldableObject): # In this case we have an already parsed and ready to go dictionary # provided by typed_kwargs if isinstance(opts, dict): - return T.cast(T.Dict[OptionKey, str], opts) + return T.cast('T.Dict[OptionKey, str]', opts) result: T.Dict[OptionKey, str] = {} overrides = stringlistify(opts) @@ -2069,8 +2069,8 @@ class SharedLibrary(BuildTarget): mlog.debug('Defaulting Rust dynamic library target crate type to "dylib"') self.rust_crate_type = 'dylib' # Don't let configuration proceed with a non-dynamic crate type - elif self.rust_crate_type not in ['dylib', 'cdylib']: - raise InvalidArguments(f'Crate type "{self.rust_crate_type}" invalid for dynamic libraries; must be "dylib" or "cdylib"') + elif self.rust_crate_type not in ['dylib', 'cdylib', 'proc-macro']: + raise InvalidArguments(f'Crate type "{self.rust_crate_type}" invalid for dynamic libraries; must be "dylib", "cdylib", or "proc-macro"') if not hasattr(self, 'prefix'): self.prefix = None if not hasattr(self, 'suffix'): @@ -2302,6 +2302,8 @@ class SharedLibrary(BuildTarget): self.rust_crate_type = rust_crate_type else: raise InvalidArguments(f'Invalid rust_crate_type "{rust_crate_type}": must be a string.') + if rust_crate_type == 'proc-macro': + FeatureNew.single_use('Rust crate type "proc-macro"', '0.62.0', self.subproject) def get_import_filename(self) -> T.Optional[str]: """ diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index a26f731..8baa727 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -622,7 +622,7 @@ class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase): def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]: # need a typeddict for this key = OptionKey('winlibs', machine=self.for_machine, lang=self.language) - return T.cast(T.List[str], options[key].value[:]) + return T.cast('T.List[str]', options[key].value[:]) def _get_options_impl(self, opts: 'KeyedOptionDictType', cpp_stds: T.List[str]) -> 'KeyedOptionDictType': key = OptionKey('std', machine=self.for_machine, lang=self.language) diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 6a595e1..c48b62c 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1055,7 +1055,7 @@ def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> Rust else: linker = type(cc.linker)(compiler, for_machine, cc.LINKER_PREFIX, always_args=always_args, version=cc.linker.version, - **extra_args) # type: ignore + **extra_args) elif 'link' in override[0]: linker = guess_win_linker(env, override, cls, for_machine, use_linker_prefix=False) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index f4365e1..04264e5 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -35,6 +35,7 @@ import typing as T if T.TYPE_CHECKING: from . import dependencies from .compilers.compilers import Compiler, CompileResult + from .dependencies.detect import TV_DepID from .environment import Environment from .mesonlib import OptionOverrideProxy, FileOrString from .cmake.traceparser import CMakeCacheEntry @@ -50,7 +51,7 @@ if T.TYPE_CHECKING: # # Pip requires that RCs are named like this: '0.1.0.rc1' # But the corresponding Git tag needs to be '0.1.0rc1' -version = '0.61.99' +version = '0.62.0.rc1' backendlist = ['ninja', 'vs', 'vs2010', 'vs2012', 'vs2013', 'vs2015', 'vs2017', 'vs2019', 'vs2022', 'xcode'] @@ -282,9 +283,6 @@ class UserFeatureOption(UserComboOption): def is_auto(self) -> bool: return self.value == 'auto' -if T.TYPE_CHECKING: - from .dependencies.detect import TV_DepID - class DependencyCacheType(enum.Enum): diff --git a/mesonbuild/dependencies/detect.py b/mesonbuild/dependencies/detect.py index f54d101..a721cba 100644 --- a/mesonbuild/dependencies/detect.py +++ b/mesonbuild/dependencies/detect.py @@ -27,6 +27,9 @@ if T.TYPE_CHECKING: from ..environment import Environment from .factory import DependencyFactory, WrappedFactoryFunc, DependencyGenerator + TV_DepIDEntry = T.Union[str, bool, int, T.Tuple[str, ...]] + TV_DepID = T.Tuple[T.Tuple[str, TV_DepIDEntry], ...] + # These must be defined in this file to avoid cyclical references. packages: T.Dict[ str, @@ -34,11 +37,6 @@ packages: T.Dict[ ] = {} _packages_accept_language: T.Set[str] = set() -if T.TYPE_CHECKING: - TV_DepIDEntry = T.Union[str, bool, int, T.Tuple[str, ...]] - TV_DepID = T.Tuple[T.Tuple[str, TV_DepIDEntry], ...] - - def get_dep_identifier(name: str, kwargs: T.Dict[str, T.Any]) -> 'TV_DepID': identifier: 'TV_DepID' = (('name', name), ) from ..interpreter import permitted_dependency_kwargs @@ -178,13 +176,13 @@ def _build_external_dependency_list(name: str, env: 'Environment', for_machine: # class method, if one exists, otherwise the list just consists of the # constructor if isinstance(packages[lname], type): - entry1 = T.cast(T.Type[ExternalDependency], packages[lname]) # mypy doesn't understand isinstance(..., type) + entry1 = T.cast('T.Type[ExternalDependency]', packages[lname]) # mypy doesn't understand isinstance(..., type) if issubclass(entry1, ExternalDependency): # TODO: somehow make mypy understand that entry1(env, kwargs) is OK... func: T.Callable[[], 'ExternalDependency'] = lambda: entry1(env, kwargs) # type: ignore dep = [func] else: - entry2 = T.cast(T.Union['DependencyFactory', 'WrappedFactoryFunc'], packages[lname]) + entry2 = T.cast('T.Union[DependencyFactory, WrappedFactoryFunc]', packages[lname]) dep = entry2(env, for_machine, kwargs) return dep diff --git a/mesonbuild/dependencies/pkgconfig.py b/mesonbuild/dependencies/pkgconfig.py index 73f2e60..55a364c 100644 --- a/mesonbuild/dependencies/pkgconfig.py +++ b/mesonbuild/dependencies/pkgconfig.py @@ -11,9 +11,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations from .base import ExternalDependency, DependencyException, sort_libpaths, DependencyTypeName -from ..mesonlib import MachineChoice, OptionKey, OrderedSet, PerMachine, Popen_safe +from ..mesonlib import OptionKey, OrderedSet, PerMachine, Popen_safe from ..programs import find_external_program, ExternalProgram from .. import mlog from pathlib import PurePath @@ -24,6 +25,7 @@ import typing as T if T.TYPE_CHECKING: from ..environment import Environment + from ..mesonlib import MachineChoice from .._typing import ImmutableListProtocol class PkgConfigDependency(ExternalDependency): diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py index a004688..f645f0e 100644 --- a/mesonbuild/dependencies/qt.py +++ b/mesonbuild/dependencies/qt.py @@ -127,13 +127,13 @@ class _QtBase: else: self.qtpkgname = self.qtname - self.private_headers = T.cast(bool, kwargs.get('private_headers', False)) + self.private_headers = T.cast('bool', kwargs.get('private_headers', False)) self.requested_modules = mesonlib.stringlistify(mesonlib.extract_as_list(kwargs, 'modules')) if not self.requested_modules: raise DependencyException('No ' + self.qtname + ' modules specified.') - self.qtmain = T.cast(bool, kwargs.get('main', False)) + self.qtmain = T.cast('bool', kwargs.get('main', False)) if not isinstance(self.qtmain, bool): raise DependencyException('"main" argument must be a boolean') diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 94127dc..0723675 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -216,7 +216,7 @@ class Properties: return res def get_java_home(self) -> T.Optional[Path]: - value = T.cast(T.Optional[str], self.properties.get('java_home')) + value = T.cast('T.Optional[str]', self.properties.get('java_home')) return Path(value) if value else None def __eq__(self, other: object) -> bool: diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index b2ddb44..5e8575a 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -47,6 +47,7 @@ from functools import lru_cache from mesonbuild import envconfig if T.TYPE_CHECKING: + import argparse from configparser import ConfigParser from .wrap.wrap import Resolver @@ -55,9 +56,6 @@ build_filename = 'meson.build' CompilersDict = T.Dict[str, Compiler] -if T.TYPE_CHECKING: - import argparse - def _get_env_var(for_machine: MachineChoice, is_cross: bool, var_name: str) -> T.Optional[str]: """ diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index bc011b7..27f4c69 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -33,10 +33,9 @@ from ..interpreterbase import InterpreterException, InvalidArguments, InvalidCod from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs from ..interpreterbase import ObjectHolder -from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs from ..modules import ExtensionModule, ModuleObject, MutableModuleObject, NewExtensionModule, NotFoundExtensionModule from ..cmake import CMakeInterpreter -from ..backend.backends import Backend, ExecutableSerialisation +from ..backend.backends import ExecutableSerialisation from . import interpreterobjects as OBJ from . import compiler as compilerOBJ @@ -96,6 +95,8 @@ if T.TYPE_CHECKING: from typing_extensions import Literal from . import kwargs + from ..backend.backends import Backend + from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs from ..programs import OverrideProgram # Input source types passed to Targets @@ -798,8 +799,6 @@ external dependencies (including libraries) must go to "dependencies".''') def func_option(self, nodes, args, kwargs): raise InterpreterException('Tried to call option() in build description file. All options must be in the option file.') - @FeatureNewKwargs('subproject', '0.38.0', ['default_options']) - @permittedKwargs({'version', 'default_options', 'required'}) @typed_pos_args('subproject', str) @typed_kwargs( 'subproject', @@ -1731,7 +1730,9 @@ external dependencies (including libraries) must go to "dependencies".''') vcs_cmd = kwargs['command'] source_dir = os.path.normpath(os.path.join(self.environment.get_source_dir(), self.subdir)) if vcs_cmd: - vcs_cmd[0] = self.find_program_impl(vcs_cmd[0]) + maincmd = self.find_program_impl(vcs_cmd[0], required=False) + if maincmd.found(): + vcs_cmd[0] = maincmd else: vcs = mesonlib.detect_vcs(source_dir) if vcs: @@ -1987,7 +1988,10 @@ external dependencies (including libraries) must go to "dependencies".''') location=node) name = name.replace(':', '_') exe = args[1] - if isinstance(exe, mesonlib.File): + if isinstance(exe, ExternalProgram): + if not exe.found(): + raise InvalidArguments('Tried to use not-found external program as test exe') + elif isinstance(exe, mesonlib.File): exe = self.find_program_impl([exe]) env = self.unpack_env_kwarg(kwargs) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index d3bab08..bf668f3 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -88,7 +88,7 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]): super().__init__(option, interpreter) if option and option.is_auto(): # TODO: we need to case here because options is not a TypedDict - self.held_object = T.cast(coredata.UserFeatureOption, self.env.coredata.options[OptionKey('auto_features')]) + self.held_object = T.cast('coredata.UserFeatureOption', self.env.coredata.options[OptionKey('auto_features')]) self.held_object.name = option.name self.methods.update({'enabled': self.enabled_method, 'disabled': self.disabled_method, diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index 4533c4a..01d0029 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -372,7 +372,7 @@ class MesonMain(MesonInterpreterObject): static: T.Optional[bool], permissive: bool = False) -> None: # We need the cast here as get_dep_identifier works on such a dict, # which FuncOverrideDependency is, but mypy can't fgure that out - nkwargs = T.cast(T.Dict[str, T.Any], kwargs.copy()) + nkwargs = T.cast('T.Dict[str, T.Any]', kwargs.copy()) if static is None: del nkwargs['static'] else: diff --git a/mesonbuild/interpreter/primitives/array.py b/mesonbuild/interpreter/primitives/array.py index e48e1c2..eeea112 100644 --- a/mesonbuild/interpreter/primitives/array.py +++ b/mesonbuild/interpreter/primitives/array.py @@ -1,5 +1,6 @@ # Copyright 2021 The Meson development team # SPDX-license-identifier: Apache-2.0 +from __future__ import annotations import typing as T @@ -15,7 +16,6 @@ from ...interpreterbase import ( FeatureNew, TYPE_var, - TYPE_kwargs, InvalidArguments, ) @@ -24,6 +24,7 @@ from ...mparser import PlusAssignmentNode if T.TYPE_CHECKING: # Object holders need the actual interpreter from ...interpreter import Interpreter + from ...interpreterbase import TYPE_kwargs class ArrayHolder(ObjectHolder[T.List[TYPE_var]], IterableObject): def __init__(self, obj: T.List[TYPE_var], interpreter: 'Interpreter') -> None: diff --git a/mesonbuild/interpreter/primitives/dict.py b/mesonbuild/interpreter/primitives/dict.py index 24f4d21..ac7c99b 100644 --- a/mesonbuild/interpreter/primitives/dict.py +++ b/mesonbuild/interpreter/primitives/dict.py @@ -1,5 +1,6 @@ # Copyright 2021 The Meson development team # SPDX-license-identifier: Apache-2.0 +from __future__ import annotations import typing as T @@ -14,7 +15,6 @@ from ...interpreterbase import ( typed_pos_args, TYPE_var, - TYPE_kwargs, InvalidArguments, ) @@ -22,6 +22,7 @@ from ...interpreterbase import ( if T.TYPE_CHECKING: # Object holders need the actual interpreter from ...interpreter import Interpreter + from ...interpreterbase import TYPE_kwargs class DictHolder(ObjectHolder[T.Dict[str, TYPE_var]], IterableObject): def __init__(self, obj: T.Dict[str, TYPE_var], interpreter: 'Interpreter') -> None: diff --git a/mesonbuild/interpreter/primitives/integer.py b/mesonbuild/interpreter/primitives/integer.py index 6563ee9..f433f57 100644 --- a/mesonbuild/interpreter/primitives/integer.py +++ b/mesonbuild/interpreter/primitives/integer.py @@ -1,5 +1,6 @@ # Copyright 2021 The Meson development team # SPDX-license-identifier: Apache-2.0 +from __future__ import annotations from ...interpreterbase import ( ObjectHolder, @@ -8,9 +9,6 @@ from ...interpreterbase import ( noKwargs, noPosargs, - TYPE_var, - TYPE_kwargs, - InvalidArguments ) @@ -19,6 +17,7 @@ import typing as T if T.TYPE_CHECKING: # Object holders need the actual interpreter from ...interpreter import Interpreter + from ...interpreterbase import TYPE_var, TYPE_kwargs class IntegerHolder(ObjectHolder[int]): def __init__(self, obj: int, interpreter: 'Interpreter') -> None: diff --git a/mesonbuild/interpreter/primitives/string.py b/mesonbuild/interpreter/primitives/string.py index 9cd51b4..9129303 100644 --- a/mesonbuild/interpreter/primitives/string.py +++ b/mesonbuild/interpreter/primitives/string.py @@ -1,5 +1,6 @@ # Copyright 2021 The Meson development team # SPDX-license-identifier: Apache-2.0 +from __future__ import annotations import re import os @@ -17,9 +18,6 @@ from ...interpreterbase import ( noPosargs, typed_pos_args, - TYPE_var, - TYPE_kwargs, - InvalidArguments, ) @@ -27,6 +25,7 @@ from ...interpreterbase import ( if T.TYPE_CHECKING: # Object holders need the actual interpreter from ...interpreter import Interpreter + from ...interpreterbase import TYPE_var, TYPE_kwargs class StringHolder(ObjectHolder[str]): def __init__(self, obj: str, interpreter: 'Interpreter') -> None: diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index cce0024..37ab192 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -11,7 +11,6 @@ from .. import compilers from ..build import (EnvironmentVariables, EnvInitValueType, CustomTarget, BuildTarget, CustomTargetIndex, ExtractedObjects, GeneratedList, IncludeDirs) from ..coredata import UserFeatureOption -from ..interpreterbase import TYPE_var from ..interpreterbase.decorators import KwargInfo, ContainerTypeInfo from ..mesonlib import File, FileMode, MachineChoice, listify, has_path_sep, OptionKey from ..programs import ExternalProgram @@ -22,6 +21,8 @@ NoneType: T.Type[None] = type(None) if T.TYPE_CHECKING: from typing_extensions import Literal + from ..interpreterbase import TYPE_var + def in_set_validator(choices: T.Set[str]) -> T.Callable[[str], T.Optional[str]]: """Check that the choice given was one of the given set.""" diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index 7186001..2e675e2 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -23,9 +23,16 @@ import typing as T from abc import ABCMeta if T.TYPE_CHECKING: + from typing_extensions import Protocol + # Object holders need the actual interpreter from ..interpreter import Interpreter + __T = T.TypeVar('__T', bound=TYPE_var, contravariant=True) + + class OperatorCall(Protocol[__T]): + def __call__(self, other: __T) -> TYPE_var: ... + TV_fw_var = T.Union[str, int, bool, list, dict, 'InterpreterObject'] TV_fw_args = T.List[T.Union[mparser.BaseNode, TV_fw_var]] TV_fw_kwargs = T.Dict[str, T.Union[mparser.BaseNode, TV_fw_var]] @@ -41,13 +48,6 @@ TYPE_key_resolver = T.Callable[[mparser.BaseNode], str] SubProject = T.NewType('SubProject', str) -if T.TYPE_CHECKING: - from typing_extensions import Protocol - __T = T.TypeVar('__T', bound=TYPE_var, contravariant=True) - - class OperatorCall(Protocol[__T]): - def __call__(self, other: __T) -> TYPE_var: ... - class InterpreterObject: def __init__(self, *, subproject: T.Optional['SubProject'] = None) -> None: self.methods: T.Dict[ diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index 7c04483..e678fd9 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -11,12 +11,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations from .. import mesonlib, mlog -from .baseobjects import TV_func, TYPE_var, TYPE_kwargs from .disabler import Disabler from .exceptions import InterpreterException, InvalidArguments -from .operator import MesonOperator from ._unholder import _unholder from dataclasses import dataclass @@ -25,9 +24,21 @@ import abc import itertools import copy import typing as T + if T.TYPE_CHECKING: + from typing_extensions import Protocol + from .. import mparser + from .baseobjects import InterpreterObject, TV_func, TYPE_var, TYPE_kwargs from .interpreterbase import SubProject + from .operator import MesonOperator + + _TV_IntegerObject = T.TypeVar('_TV_IntegerObject', bound=InterpreterObject, contravariant=True) + _TV_ARG1 = T.TypeVar('_TV_ARG1', bound=TYPE_var, contravariant=True) + + class FN_Operator(Protocol[_TV_IntegerObject, _TV_ARG1]): + def __call__(s, self: _TV_IntegerObject, other: _TV_ARG1) -> TYPE_var: ... + _TV_FN_Operator = T.TypeVar('_TV_FN_Operator', bound=FN_Operator) def get_callee_args(wrapped_args: T.Sequence[T.Any]) -> T.Tuple['mparser.BaseNode', T.List['TYPE_var'], 'TYPE_kwargs', 'SubProject']: # First argument could be InterpreterBase, InterpreterObject or ModuleObject. @@ -51,7 +62,7 @@ def noPosargs(f: TV_func) -> TV_func: if args: raise InvalidArguments('Function does not take positional arguments.') return f(*wrapped_args, **wrapped_kwargs) - return T.cast(TV_func, wrapped) + return T.cast('TV_func', wrapped) def noKwargs(f: TV_func) -> TV_func: @wraps(f) @@ -60,7 +71,7 @@ def noKwargs(f: TV_func) -> TV_func: if kwargs: raise InvalidArguments('Function does not take keyword arguments.') return f(*wrapped_args, **wrapped_kwargs) - return T.cast(TV_func, wrapped) + return T.cast('TV_func', wrapped) def stringArgs(f: TV_func) -> TV_func: @wraps(f) @@ -73,7 +84,7 @@ def stringArgs(f: TV_func) -> TV_func: mlog.debug('Element not a string:', str(args)) raise InvalidArguments('Arguments must be strings.') return f(*wrapped_args, **wrapped_kwargs) - return T.cast(TV_func, wrapped) + return T.cast('TV_func', wrapped) def noArgsFlattening(f: TV_func) -> TV_func: setattr(f, 'no-args-flattening', True) # noqa: B010 @@ -88,7 +99,7 @@ def unholder_return(f: TV_func) -> T.Callable[..., TYPE_var]: def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: res = f(*wrapped_args, **wrapped_kwargs) return _unholder(res) - return T.cast(T.Callable[..., TYPE_var], wrapped) + return T.cast('T.Callable[..., TYPE_var]', wrapped) def disablerIfNotFound(f: TV_func) -> TV_func: @wraps(f) @@ -99,7 +110,7 @@ def disablerIfNotFound(f: TV_func) -> TV_func: if disabler and not ret.found(): return Disabler() return ret - return T.cast(TV_func, wrapped) + return T.cast('TV_func', wrapped) @dataclass(repr=False, eq=False) class permittedKwargs: @@ -114,18 +125,7 @@ class permittedKwargs: ustr = ', '.join([f'"{u}"' for u in sorted(unknowns)]) raise InvalidArguments(f'Got unknown keyword arguments {ustr}') return f(*wrapped_args, **wrapped_kwargs) - return T.cast(TV_func, wrapped) - -if T.TYPE_CHECKING: - from .baseobjects import InterpreterObject - from typing_extensions import Protocol - - _TV_IntegerObject = T.TypeVar('_TV_IntegerObject', bound=InterpreterObject, contravariant=True) - _TV_ARG1 = T.TypeVar('_TV_ARG1', bound=TYPE_var, contravariant=True) - - class FN_Operator(Protocol[_TV_IntegerObject, _TV_ARG1]): - def __call__(s, self: _TV_IntegerObject, other: _TV_ARG1) -> TYPE_var: ... - _TV_FN_Operator = T.TypeVar('_TV_FN_Operator', bound=FN_Operator) + return T.cast('TV_func', wrapped) def typed_operator(operator: MesonOperator, types: T.Union[T.Type, T.Tuple[T.Type, ...]]) -> T.Callable[['_TV_FN_Operator'], '_TV_FN_Operator']: @@ -276,7 +276,7 @@ def typed_pos_args(name: str, *types: T.Union[T.Type, T.Tuple[T.Type, ...]], nargs[i] = tuple(args) return f(*nargs, **wrapped_kwargs) - return T.cast(TV_func, wrapper) + return T.cast('TV_func', wrapper) return inner @@ -521,7 +521,7 @@ def typed_kwargs(name: str, *types: KwargInfo) -> T.Callable[..., T.Any]: node, _, _kwargs, subproject = get_callee_args(wrapped_args) # Cast here, as the convertor function may place something other than a TYPE_var in the kwargs - kwargs = T.cast(T.Dict[str, object], _kwargs) + kwargs = T.cast('T.Dict[str, object]', _kwargs) all_names = {t.name for t in types} unknowns = set(kwargs).difference(all_names) @@ -572,7 +572,7 @@ def typed_kwargs(name: str, *types: KwargInfo) -> T.Callable[..., T.Any]: kwargs[info.name] = info.convertor(kwargs[info.name]) return f(*wrapped_args, **wrapped_kwargs) - return T.cast(TV_func, wrapper) + return T.cast('TV_func', wrapper) return inner @@ -664,7 +664,7 @@ class FeatureCheckBase(metaclass=abc.ABCMeta): raise AssertionError(f'{wrapped_args!r}') self.use(subproject, node) return f(*wrapped_args, **wrapped_kwargs) - return T.cast(TV_func, wrapped) + return T.cast('TV_func', wrapped) @classmethod def single_use(cls, feature_name: str, version: str, subproject: 'SubProject', @@ -766,7 +766,7 @@ class FeatureCheckKwargsBase(metaclass=abc.ABCMeta): self.feature_check_class.single_use( name, self.feature_version, subproject, self.extra_message, node) return f(*wrapped_args, **wrapped_kwargs) - return T.cast(TV_func, wrapped) + return T.cast('TV_func', wrapped) class FeatureNewKwargs(FeatureCheckKwargsBase): feature_check_class = FeatureNew diff --git a/mesonbuild/interpreterbase/disabler.py b/mesonbuild/interpreterbase/disabler.py index 63b914e..182bb62 100644 --- a/mesonbuild/interpreterbase/disabler.py +++ b/mesonbuild/interpreterbase/disabler.py @@ -11,10 +11,15 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations -from .baseobjects import MesonInterpreterObject, TYPE_var, TYPE_kwargs import typing as T +from .baseobjects import MesonInterpreterObject + +if T.TYPE_CHECKING: + from .baseobjects import TYPE_var, TYPE_kwargs + class Disabler(MesonInterpreterObject): def method_call(self, method_name: str, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> TYPE_var: if method_name == 'found': diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 28211b0..85aabd1 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -14,6 +14,7 @@ # This class contains the basic functionality needed to run any interpreter # or an interpreter-based tool. +from __future__ import annotations from .. import mparser, mesonlib from .. import environment @@ -26,10 +27,7 @@ from .baseobjects import ( ObjectHolder, IterableObject, - SubProject, - TYPE_var, - TYPE_kwargs, HoldableTypes, ) @@ -54,6 +52,7 @@ import typing as T import textwrap if T.TYPE_CHECKING: + from .baseobjects import SubProject, TYPE_kwargs from ..interpreter import Interpreter HolderMapType = T.Dict[ diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py index 0bfd708..80e0948 100644 --- a/mesonbuild/linkers/detect.py +++ b/mesonbuild/linkers/detect.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from ..mesonlib import ( - EnvironmentException, MachineChoice, OptionKey, + EnvironmentException, OptionKey, Popen_safe, search_version ) from .linkers import ( - DynamicLinker, AppleDynamicLinker, - GnuDynamicLinker, GnuGoldDynamicLinker, GnuBFDDynamicLinker, LLVMDynamicLinker, @@ -36,8 +36,10 @@ import shlex import typing as T if T.TYPE_CHECKING: + from .linkers import DynamicLinker, GnuDynamicLinker from ..environment import Environment from ..compilers import Compiler + from ..mesonlib import MachineChoice defaults: T.Dict[str, T.List[str]] = {} defaults['static_linker'] = ['ar', 'gar'] diff --git a/mesonbuild/linkers/linkers.py b/mesonbuild/linkers/linkers.py index 9cc217a..88b66be 100644 --- a/mesonbuild/linkers/linkers.py +++ b/mesonbuild/linkers/linkers.py @@ -1182,7 +1182,7 @@ class VisualStudioLikeLinkerMixin: def get_always_args(self) -> T.List[str]: parent = super().get_always_args() # type: ignore - return self._apply_prefix('/nologo') + T.cast(T.List[str], parent) + return self._apply_prefix('/nologo') + T.cast('T.List[str]', parent) def get_search_args(self, dirname: str) -> T.List[str]: return self._apply_prefix('/LIBPATH:' + dirname) diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index 99bcd32..d4f51c1 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -900,7 +900,7 @@ def version_compare_condition_with_min(condition: str, minimum: str) -> bool: if re.match(r'^\d+.\d+$', condition): condition += '.0' - return T.cast(bool, cmpop(Version(minimum), Version(condition))) + return T.cast('bool', cmpop(Version(minimum), Version(condition))) def search_version(text: str) -> str: # Usually of the type 4.1.4 but compiler output may contain @@ -1346,7 +1346,7 @@ def typeslistify(item: 'T.Union[_T, T.Sequence[_T]]', list of items all of which are of type @types ''' if isinstance(item, types): - item = T.cast(T.List[_T], [item]) + item = T.cast('T.List[_T]', [item]) if not isinstance(item, list): raise MesonException('Item must be a list or one of {!r}, not {!r}'.format(types, type(item))) for i in item: diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 93cb8b0..89816ec 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -30,7 +30,7 @@ from . import mconf, mdist, minit, minstall, mintro, msetup, mtest, rewriter, ms from .mesonlib import MesonException, MesonBugException from .environment import detect_msys2_arch from .wrap import wraptool - +from .scripts import env2mfile # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ @@ -70,6 +70,8 @@ class CommandLineParser: help_msg='Build the project') self.add_command('devenv', mdevenv.add_arguments, mdevenv.run, help_msg='Run commands in developer environment') + self.add_command('env2mfile', env2mfile.add_arguments, env2mfile.run, + help_msg='Convert current environment to a cross or native file') # Hidden commands self.add_command('runpython', self.add_runpython_arguments, self.run_runpython_command, diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index b51d4f0..f33ad0a 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -61,6 +61,7 @@ if T.TYPE_CHECKING: dry_run: bool skip_subprojects: str tags: str + strip: bool symlink_warning = '''Warning: trying to copy a symlink that points to a file. This will copy the file, @@ -88,6 +89,8 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: help='Do not install files from given subprojects. (Since 0.58.0)') parser.add_argument('--tags', default=None, help='Install only targets having one of the given tags. (Since 0.60.0)') + parser.add_argument('--strip', action='store_true', + help='Strip targets even if strip option was not set during configure. (Since 0.62.0)') class DirMaker: def __init__(self, lf: T.TextIO, makedirs: T.Callable[..., None]): @@ -564,6 +567,15 @@ class Installer: else: raise + def do_strip(self, strip_bin: T.List[str], fname: str, outname: str) -> None: + self.log(f'Stripping target {fname!r}.') + returncode, stdo, stde = self.Popen_safe(strip_bin + [outname]) + if returncode != 0: + print('Could not strip file.\n') + print(f'Stdout:\n{stdo}\n') + print(f'Stderr:\n{stde}\n') + sys.exit(1) + def install_subdirs(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None: for i in d.install_subdirs: if not self.should_install(i): @@ -676,7 +688,7 @@ class Installer: outdir = get_destdir_path(destdir, fullprefix, t.outdir) outname = os.path.join(outdir, os.path.basename(fname)) final_path = os.path.join(d.prefix, t.outdir, os.path.basename(fname)) - should_strip = t.strip + should_strip = t.strip or (t.can_strip and self.options.strip) install_rpath = t.install_rpath install_name_mappings = t.install_name_mappings install_mode = t.install_mode @@ -689,13 +701,7 @@ class Installer: if fname.endswith('.jar'): self.log('Not stripping jar target: {}'.format(os.path.basename(fname))) continue - self.log('Stripping target {!r} using {}.'.format(fname, d.strip_bin[0])) - returncode, stdo, stde = self.Popen_safe(d.strip_bin + [outname]) - if returncode != 0: - print('Could not strip file.\n') - print(f'Stdout:\n{stdo}\n') - print(f'Stderr:\n{stde}\n') - sys.exit(1) + self.do_strip(d.strip_bin, fname, outname) if fname.endswith('.js'): # Emscripten outputs js files and optionally a wasm file. # If one was generated, install it as well. @@ -721,7 +727,6 @@ class Installer: else: raise - def rebuild_all(wd: str) -> bool: if not (Path(wd) / 'build.ninja').is_file(): print('Only ninja backend is supported to rebuild the project before installation.') diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index bdc9da5..d3ef68d 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -320,7 +320,7 @@ def _log_error(severity: str, *rargs: TV_Loggable, location_str = get_error_location_string(location_file, location.lineno) # Unions are frankly awful, and we have to T.cast here to get mypy # to understand that the list concatenation is safe - location_list = T.cast(TV_LoggableList, [location_str]) + location_list = T.cast('TV_LoggableList', [location_str]) args = location_list + args log(*args, once=once, **kwargs) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index d1729ef..f908a83 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1168,7 +1168,7 @@ class GnomeModule(ExtensionModule): scan_target = self._make_gir_target( state, girfile, scan_command, generated_files, depends, # We have to cast here because mypy can't figure this out - T.cast(T.Dict[str, T.Any], kwargs)) + T.cast('T.Dict[str, T.Any]', kwargs)) typelib_output = f'{ns}-{nsversion}.typelib' typelib_cmd = [gicompiler, scan_target, '--output', '@OUTPUT@'] @@ -1177,7 +1177,7 @@ class GnomeModule(ExtensionModule): for incdir in typelib_includes: typelib_cmd += ["--includedir=" + incdir] - typelib_target = self._make_typelib_target(state, typelib_output, typelib_cmd, generated_files, T.cast(T.Dict[str, T.Any], kwargs)) + typelib_target = self._make_typelib_target(state, typelib_output, typelib_cmd, generated_files, T.cast('T.Dict[str, T.Any]', kwargs)) self._devenv_prepend('GI_TYPELIB_PATH', os.path.join(state.environment.get_build_dir(), state.subdir)) @@ -1417,8 +1417,10 @@ class GnomeModule(ExtensionModule): t_args.append(f'--{program_name}={path}') if namespace: t_args.append('--namespace=' + namespace) - if state.environment.need_exe_wrapper() and not isinstance(state.environment.get_exe_wrapper(), EmptyExternalProgram): - t_args.append('--run=' + ' '.join(state.environment.get_exe_wrapper().get_command())) + # if not need_exe_wrapper, we get an EmptyExternalProgram. If none provided, we get NoneType + exe_wrapper = state.environment.get_exe_wrapper() + if not isinstance(exe_wrapper, (NoneType, EmptyExternalProgram)): + t_args.append('--run=' + ' '.join(exe_wrapper.get_command())) t_args.append(f'--htmlargs={"@@".join(kwargs["html_args"])}') t_args.append(f'--scanargs={"@@".join(kwargs["scan_args"])}') t_args.append(f'--scanobjsargs={"@@".join(kwargs["scanobjs_args"])}') diff --git a/mesonbuild/modules/java.py b/mesonbuild/modules/java.py index 6884a22..ddb14c8 100644 --- a/mesonbuild/modules/java.py +++ b/mesonbuild/modules/java.py @@ -16,7 +16,7 @@ import os import pathlib import typing as T from mesonbuild import mesonlib -from mesonbuild.build import CustomTarget +from mesonbuild.build import CustomTarget, CustomTargetIndex, GeneratedList, Target from mesonbuild.compilers import detect_compiler_for, Compiler from mesonbuild.interpreter import Interpreter from mesonbuild.interpreterbase.decorators import ContainerTypeInfo, FeatureDeprecated, FeatureNew, KwargInfo, typed_pos_args, typed_kwargs @@ -80,14 +80,15 @@ class JavaModule(NewExtensionModule): return ModuleReturnValue(target, [target]) @FeatureNew('java.generate_native_headers', '0.62.0') - @typed_pos_args('java.generate_native_headers', (str, mesonlib.File), min_varargs=1) + @typed_pos_args('java.generate_native_headers', + varargs=(str, mesonlib.File, Target, CustomTargetIndex, GeneratedList)) @typed_kwargs('java.generate_native_headers', - KwargInfo('classes', (ContainerTypeInfo(list, str)), default=[], listify=True, + KwargInfo('classes', ContainerTypeInfo(list, str), default=[], listify=True, required=True), KwargInfo('package', str, default=None)) - def generate_native_headers(self, state: ModuleState, args: T.List[mesonlib.FileOrString], + def generate_native_headers(self, state: ModuleState, args: T.Tuple[T.List[mesonlib.FileOrString]], kwargs: T.Dict[str, T.Optional[str]]) -> ModuleReturnValue: - classes = T.cast(T.List[str], kwargs.get('classes')) + classes = T.cast('T.List[str]', kwargs.get('classes')) package = kwargs.get('package') headers: T.List[str] = [] @@ -112,7 +113,7 @@ class JavaModule(NewExtensionModule): prefix = classes[0] if not package else package target = CustomTarget(f'{prefix}-native-headers', state.subdir, state.subproject, command, - sources=list(args), outputs=headers, backend=state.backend) + sources=args[0], outputs=headers, backend=state.backend) # It is only known that 1.8.0 won't pre-create the directory. 11 and 16 # do not exhibit this behavior. diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index f67e6eb..0eac360 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -524,6 +524,7 @@ class PkgConfigModule(ExtensionModule): blocked_vars = ['libraries', 'libraries_private', 'require_private', 'extra_cflags', 'subdirs'] if any(k in kwargs for k in blocked_vars): raise mesonlib.MesonException(f'Cannot combine dataonly with any of {blocked_vars}') + default_install_dir = os.path.join(state.environment.get_datadir(), 'pkgconfig') subdirs = mesonlib.stringlistify(kwargs.get('subdirs', default_subdirs)) version = kwargs.get('version', default_version) diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index f149ca1..3eac33f 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -38,28 +38,52 @@ from ..mesonlib import MachineChoice from ..programs import ExternalProgram, NonExistingExternalProgram if T.TYPE_CHECKING: + from typing_extensions import TypedDict + from . import ModuleState from ..build import SharedModule, Data from ..dependencies import ExternalDependency, Dependency from ..dependencies.factory import DependencyGenerator from ..environment import Environment from ..interpreter import Interpreter + from ..interpreter.kwargs import ExtractRequired from ..interpreterbase.interpreterbase import TYPE_var, TYPE_kwargs from ..backends import InstallData - from typing_extensions import TypedDict + class PythonIntrospectionDict(TypedDict): + install_paths: T.Dict[str, str] + is_pypy: bool + is_venv: bool + link_libpython: bool + sysconfig_paths: T.Dict[str, str] + paths: T.Dict[str, str] + platform: str + suffix: str + variables: T.Dict[str, str] + version: str -mod_kwargs = {'subdir'} -mod_kwargs.update(known_shmod_kwargs) -mod_kwargs -= {'name_prefix', 'name_suffix'} + class PyInstallKw(TypedDict): + + pure: bool + subdir: str + install_tag: T.Optional[str] + class FindInstallationKw(ExtractRequired): + + disabler: bool + modules: T.List[str] -if T.TYPE_CHECKING: _Base = ExternalDependency else: _Base = object + +mod_kwargs = {'subdir'} +mod_kwargs.update(known_shmod_kwargs) +mod_kwargs -= {'name_prefix', 'name_suffix'} + + class _PythonDependencyBase(_Base): def __init__(self, python_holder: 'PythonInstallation', embed: bool): @@ -134,7 +158,7 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase): if largs is not None: self.link_args = largs - self.is_found = largs is not None or self.link_libpython + self.is_found = largs is not None or not self.link_libpython inc_paths = mesonlib.OrderedSet([ self.variables.get('INCLUDEPY'), @@ -350,20 +374,6 @@ print(json.dumps({ })) ''' -if T.TYPE_CHECKING: - class PythonIntrospectionDict(TypedDict): - - install_paths: T.Dict[str, str] - is_pypy: bool - is_venv: bool - link_libpython: bool - sysconfig_paths: T.Dict[str, str] - paths: T.Dict[str, str] - platform: str - suffix: str - variables: T.Dict[str, str] - version: str - class PythonExternalProgram(ExternalProgram): def __init__(self, name: str, command: T.Optional[T.List[str]] = None, @@ -404,7 +414,6 @@ class PythonExternalProgram(ExternalProgram): if subdir_parts[-len(install_subdir_parts):] == install_subdir_parts: pypath = os.path.join(basedir, *subdir_parts[:-len(install_subdir_parts)]) self.devenv_pythonpath.add(pypath) - print('done', pypath) def _check_version(self, version: str) -> bool: if self.name == 'python2': @@ -474,14 +483,6 @@ class PythonExternalProgram(ExternalProgram): _PURE_KW = KwargInfo('pure', bool, default=True) _SUBDIR_KW = KwargInfo('subdir', str, default='') -if T.TYPE_CHECKING: - - class PyInstallKw(TypedDict): - - pure: bool - subdir: str - install_tag: T.Optional[str] - class PythonInstallation(ExternalProgramHolder): def __init__(self, python: 'PythonExternalProgram', interpreter: 'Interpreter'): @@ -649,15 +650,6 @@ class PythonInstallation(ExternalProgramHolder): return super().path_method(args, kwargs) -if T.TYPE_CHECKING: - from ..interpreter.kwargs import ExtractRequired - - class FindInstallationKw(ExtractRequired): - - disabler: bool - modules: T.List[str] - - class PythonModule(ExtensionModule): @FeatureNew('Python Module', '0.46.0') diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py index 37072b4..bf6d30c 100644 --- a/mesonbuild/modules/qt.py +++ b/mesonbuild/modules/qt.py @@ -486,7 +486,7 @@ class QtBaseModule(ExtensionModule): if _sources: FeatureDeprecated.single_use('qt.preprocess positional sources', '0.59', state.subproject, location=state.current_node) # List is invariant, os we have to cast... - sources = T.cast(T.List[T.Union[str, File, build.GeneratedList, build.CustomTarget]], + sources = T.cast('T.List[T.Union[str, File, build.GeneratedList, build.CustomTarget]]', _sources + kwargs['sources']) for s in sources: if not isinstance(s, (str, File)): diff --git a/mesonbuild/scripts/env2mfile.py b/mesonbuild/scripts/env2mfile.py new file mode 100755 index 0000000..9441402 --- /dev/null +++ b/mesonbuild/scripts/env2mfile.py @@ -0,0 +1,368 @@ +# Copyright 2022 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys, os, subprocess, shutil +import shlex + +import argparse +from typing import TextIO, Dict, List, Union, Tuple, Any, Optional + +from .. import mlog + +UNIXY_ENVVARS_COMPILER = {'c': 'CC', + 'cpp': 'CXX', + 'objc': 'OBJCC', + 'objcpp': 'OBJCXX', + 'fortran': 'FC', + 'rust': 'RUSTC', + 'vala': 'VALAC', + 'cs': 'CSC', + } + +UNIXY_ENVVARS_TOOLS = {'ar': 'AR', + 'strip': 'STRIP', + 'windres': 'WINDRES', + 'pkgconfig': 'PKG_CONFIG', + 'vapigen': 'VAPIGEN', + 'cmake': 'CMAKE', + 'qmake': 'QMAKE', + } + +UNIXY_ENVVARS_FLAGS = {'c': 'CFLAGS', + 'cpp': 'CXXFLAGS', + 'objc': 'OBJCFLAGS', + 'objcpp': 'OBJCXXFLAGS', + 'fortran': 'FFLAGS', + 'rust': 'RUSTFLAGS', + 'vala': 'VALAFLAGS', + 'cs': 'CSFLAGS', # This one might not be standard. + } + +TYPICAL_UNIXY_COMPILER_NAMES = {'c': ['cc', 'gcc', 'clang'], + 'cpp': ['c++', 'g++', 'clang++'], + 'objc': ['objc', 'clang'], + 'objcpp': ['objcpp', 'clang++'], + 'fortran': ['gfortran'], + } + +LANGS_USING_CPPFLAGS = {'c', 'cpp', 'objc', 'objcxx'} + +def has_for_build() -> bool: + for cenv in UNIXY_ENVVARS_COMPILER.values(): + if os.environ.get(cenv + '_FOR_BUILD'): + return True + return False + +def add_arguments(parser: 'argparse.ArgumentParser') -> None: + parser.add_argument('--debarch', default=None, + help='The dpkg architecture to generate.') + parser.add_argument('--gccsuffix', default="", + help='A particular gcc version suffix if necessary.') + parser.add_argument('-o', required=True, dest='outfile', + help='The output file.') + parser.add_argument('--cross', default=False, action='store_true', + help='Generate a cross compilation file.') + parser.add_argument('--native', default=False, action='store_true', + help='Generate a native compilation file.') + parser.add_argument('--system', default=None, + help='Define system for cross compilation.') + parser.add_argument('--cpu', default=None, + help='Define cpu for cross compilation.') + parser.add_argument('--cpu-family', default=None, + help='Define cpu family for cross compilation.') + parser.add_argument('--endian', default='little', choices=['big', 'little'], + help='Define endianness for cross compilation.') + +class MachineInfo: + def __init__(self) -> None: + self.compilers: Dict[str, List[str]] = {} + self.binaries: Dict[str, List[str]] = {} + self.properties: Dict[str, Union[str, List[str]]] = {} + self.compile_args: Dict[str, List[str]] = {} + self.link_args: Dict[str, List[str]] = {} + + self.system: Optional[str] = None + self.cpu: Optional[str] = None + self.cpu_family: Optional[str] = None + self.endian: Optional[str] = None + +#parser = argparse.ArgumentParser(description='''Generate cross compilation definition file for the Meson build system. +# +#If you do not specify the --arch argument, Meson assumes that running +#plain 'dpkg-architecture' will return correct information for the +#host system. +# +#This script must be run in an environment where CPPFLAGS et al are set to the +#same values used in the actual compilation. +#''' +#) + +def locate_path(program: str) -> List[str]: + if os.path.isabs(program): + return [program] + for d in os.get_exec_path(): + f = os.path.join(d, program) + if os.access(f, os.X_OK): + return [f] + raise ValueError("%s not found on $PATH" % program) + +def write_args_line(ofile: TextIO, name: str, args: List[str]) -> None: + if len(args) == 0: + return + ostr = name + ' = [' + ostr += ', '.join("'" + i + "'" for i in args) + ostr += ']\n' + ofile.write(ostr) + +def get_args_from_envvars(infos: MachineInfo) -> None: + cppflags = shlex.split(os.environ.get('CPPFLAGS', '')) + cflags = shlex.split(os.environ.get('CFLAGS', '')) + cxxflags = shlex.split(os.environ.get('CXXFLAGS', '')) + objcflags = shlex.split(os.environ.get('OBJCFLAGS', '')) + objcxxflags = shlex.split(os.environ.get('OBJCXXFLAGS', '')) + ldflags = shlex.split(os.environ.get('LDFLAGS', '')) + + c_args = cppflags + cflags + cpp_args = cppflags + cxxflags + c_link_args = cflags + ldflags + cpp_link_args = cxxflags + ldflags + + objc_args = cppflags + objcflags + objcpp_args = cppflags + objcxxflags + objc_link_args = objcflags + ldflags + objcpp_link_args = objcxxflags + ldflags + + if c_args: + infos.compile_args['c'] = c_args + if c_link_args: + infos.link_args['c'] = c_link_args + if cpp_args: + infos.compile_args['cpp'] = cpp_args + if cpp_link_args: + infos.link_args['cpp'] = cpp_link_args + if objc_args: + infos.compile_args['objc'] = objc_args + if objc_link_args: + infos.link_args['objc'] = objc_link_args + if objcpp_args: + infos.compile_args['objcpp'] = objcpp_args + if objcpp_link_args: + infos.link_args['objcpp'] = objcpp_link_args + +cpu_family_map = dict(mips64el="mips64", + i686='x86') +cpu_map = dict(armhf="arm7hlf", + mips64el="mips64",) + +def deb_compiler_lookup(infos: MachineInfo, compilerstems: List[Tuple[str, str]], host_arch: str, gccsuffix: str) -> None: + for langname, stem in compilerstems: + compilername = f'{host_arch}-{stem}{gccsuffix}' + try: + p = locate_path(compilername) + infos.compilers[langname] = p + except ValueError: + pass + +def detect_cross_debianlike(options: Any) -> MachineInfo: + if options.debarch is None: + cmd = ['dpkg-architecture'] + else: + cmd = ['dpkg-architecture', '-a' + options.debarch] + output = subprocess.check_output(cmd, universal_newlines=True, + stderr=subprocess.DEVNULL) + data = {} + for line in output.split('\n'): + line = line.strip() + if line == '': + continue + k, v = line.split('=', 1) + data[k] = v + host_arch = data['DEB_HOST_GNU_TYPE'] + host_os = data['DEB_HOST_ARCH_OS'] + host_cpu_family = cpu_family_map.get(data['DEB_HOST_GNU_CPU'], + data['DEB_HOST_GNU_CPU']) + host_cpu = cpu_map.get(data['DEB_HOST_ARCH'], + data['DEB_HOST_ARCH']) + host_endian = data['DEB_HOST_ARCH_ENDIAN'] + + compilerstems = [('c', 'gcc'), + ('cpp', 'h++'), + ('objc', 'gobjc'), + ('objcpp', 'gobjc++')] + infos = MachineInfo() + deb_compiler_lookup(infos, compilerstems, host_arch, options.gccsuffix) + if len(infos.compilers) == 0: + print('Warning: no compilers were detected.') + infos.binaries['ar'] = locate_path("%s-ar" % host_arch) + infos.binaries['strip'] = locate_path("%s-strip" % host_arch) + infos.binaries['objcopy'] = locate_path("%s-objcopy" % host_arch) + infos.binaries['ld'] = locate_path("%s-ld" % host_arch) + try: + infos.binaries['pkgconfig'] = locate_path("%s-pkg-config" % host_arch) + except ValueError: + pass # pkg-config is optional + try: + infos.binaries['cups-config'] = locate_path("cups-config") + except ValueError: + pass + infos.system = host_os + infos.cpu_family = host_cpu_family + infos.cpu = host_cpu + infos.endian = host_endian + + get_args_from_envvars(infos) + return infos + +def write_machine_file(infos: MachineInfo, ofilename: str, write_system_info: bool) -> None: + tmpfilename = ofilename + '~' + with open(tmpfilename, 'w') as ofile: + ofile.write('[binaries]\n') + ofile.write('# Compilers\n') + for langname in sorted(infos.compilers.keys()): + compiler = infos.compilers[langname] + write_args_line(ofile, langname, compiler) + ofile.write('\n') + + ofile.write('# Other binaries\n') + for exename in sorted(infos.binaries.keys()): + exe = infos.binaries[exename] + write_args_line(ofile, exename, exe) + ofile.write('\n') + + ofile.write('[properties]\n') + all_langs = list(set(infos.compile_args.keys()).union(set(infos.link_args.keys()))) + all_langs.sort() + for lang in all_langs: + if lang in infos.compile_args: + write_args_line(ofile, lang + '_args', infos.compile_args[lang]) + if lang in infos.link_args: + write_args_line(ofile, lang + '_link_args', infos.link_args[lang]) + ofile.write('\n') + + if write_system_info: + ofile.write('[host_machine]\n') + ofile.write(f"cpu = '{infos.cpu}'\n") + ofile.write(f"cpu_family = '{infos.cpu_family}'\n") + ofile.write(f"endian = '{infos.endian}'\n") + ofile.write(f"system = '{infos.system}'\n") + os.replace(tmpfilename, ofilename) + +def detect_language_args_from_envvars(langname: str, envvar_suffix: str ='') -> Tuple[List[str], List[str]]: + ldflags = tuple(shlex.split(os.environ.get('LDFLAGS' + envvar_suffix, ''))) + compile_args = shlex.split(os.environ.get(UNIXY_ENVVARS_FLAGS[langname] + envvar_suffix, '')) + if langname in LANGS_USING_CPPFLAGS: + cppflags = tuple(shlex.split(os.environ.get('CPPFLAGS' + envvar_suffix, ''))) + lang_compile_args = list(cppflags) + compile_args + else: + lang_compile_args = compile_args + lang_link_args = list(ldflags) + compile_args + return (lang_compile_args, lang_link_args) + +def detect_compilers_from_envvars(envvar_suffix:str ='') -> MachineInfo: + infos = MachineInfo() + for langname, envvarname in UNIXY_ENVVARS_COMPILER.items(): + compilerstr = os.environ.get(envvarname + envvar_suffix) + if not compilerstr: + continue + compiler = shlex.split(compilerstr) + infos.compilers[langname] = compiler + lang_compile_args, lang_link_args = detect_language_args_from_envvars(langname, envvar_suffix) + if lang_compile_args: + infos.compile_args[langname] = lang_compile_args + if lang_link_args: + infos.link_args[langname] = lang_link_args + return infos + +def detect_binaries_from_envvars(infos: MachineInfo, envvar_suffix:str ='') -> None: + for binname, envvar_base in UNIXY_ENVVARS_TOOLS.items(): + envvar = envvar_base + envvar_suffix + binstr = os.environ.get(envvar) + if binstr: + infos.binaries[binname] = shlex.split(binstr) + +def detect_cross_system(infos: MachineInfo, options: Any) -> None: + for optname in ('system', 'cpu', 'cpu_family', 'endian'): + v = getattr(options, optname) + if not v: + mlog.error(f'Cross property "{optname}" missing, set it with --{optname.replace("_", "-")}.') + sys.exit(1) + setattr(infos, optname, v) + +def detect_cross_env(options: Any) -> MachineInfo: + if options.debarch: + print('Detecting cross environment via dpkg-reconfigure.') + infos = detect_cross_debianlike(options) + else: + print('Detecting cross environment via environment variables.') + infos = detect_compilers_from_envvars() + detect_cross_system(infos, options) + return infos + +def add_compiler_if_missing(infos: MachineInfo, langname: str, exe_names: List[str]) -> None: + if langname in infos.compilers: + return + for exe_name in exe_names: + lookup = shutil.which(exe_name) + if not lookup: + continue + compflags, linkflags = detect_language_args_from_envvars(langname) + infos.compilers[langname] = [lookup] + if compflags: + infos.compile_args[langname] = compflags + if linkflags: + infos.link_args[langname] = linkflags + return + +def detect_missing_native_compilers(infos: MachineInfo) -> None: + # Any per-platform special detection should go here. + for langname, exes in TYPICAL_UNIXY_COMPILER_NAMES.items(): + add_compiler_if_missing(infos, langname, exes) + +def detect_missing_native_binaries(infos: MachineInfo) -> None: + # Any per-platform special detection should go here. + for toolname in sorted(UNIXY_ENVVARS_TOOLS.keys()): + if toolname in infos.binaries: + continue + exe = shutil.which(toolname) + if exe: + infos.binaries[toolname] = [exe] + +def detect_native_env(options: Any) -> MachineInfo: + use_for_build = has_for_build() + if use_for_build: + mlog.log('Using FOR_BUILD envvars for detection') + esuffix = '_FOR_BUILD' + else: + mlog.log('Using regular envvars for detection.') + esuffix = '' + infos = detect_compilers_from_envvars(esuffix) + detect_missing_native_compilers(infos) + detect_binaries_from_envvars(infos, esuffix) + detect_missing_native_binaries(infos) + return infos + +def run(options: Any) -> None: + if options.cross and options.native: + sys.exit('You can only specify either --cross or --native, not both.') + if not options.cross and not options.native: + sys.exit('You must specify --cross or --native.') + mlog.notice('This functionality is experimental and subject to change.') + detect_cross = options.cross + if detect_cross: + infos = detect_cross_env(options) + write_system_info = True + else: + infos = detect_native_env(options) + write_system_info = False + write_machine_file(infos, options.outfile, write_system_info) diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py index 664bfec..ca14315 100644 --- a/mesonbuild/wrap/wraptool.py +++ b/mesonbuild/wrap/wraptool.py @@ -60,7 +60,7 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: def get_releases() -> T.Dict[str, T.Any]: url = urlopen('https://wrapdb.mesonbuild.com/v2/releases.json') - return T.cast(T.Dict[str, T.Any], json.loads(url.read().decode())) + return T.cast('T.Dict[str, T.Any]', json.loads(url.read().decode())) def list_projects(options: 'argparse.Namespace') -> None: releases = get_releases() diff --git a/test cases/common/44 pkgconfig-gen/test.json b/test cases/common/44 pkgconfig-gen/test.json index 118fecd..4630a02 100644 --- a/test cases/common/44 pkgconfig-gen/test.json +++ b/test cases/common/44 pkgconfig-gen/test.json @@ -6,7 +6,6 @@ {"type": "file", "file": "usr/lib/pkgconfig/libanswer.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libfoo.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libhello.pc"}, - {"type": "file", "file": "usr/lib/pkgconfig/libhello_nolib.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libvartest.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/libvartest2.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/simple2.pc"}, @@ -14,7 +13,8 @@ {"type": "file", "file": "usr/lib/pkgconfig/simple5.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/simple6.pc"}, {"type": "file", "file": "usr/lib/pkgconfig/ct.pc"}, - {"type": "file", "file": "usr/lib/pkgconfig/ct0.pc"} + {"type": "file", "file": "usr/lib/pkgconfig/ct0.pc"}, + {"type": "file", "file": "usr/share/pkgconfig/libhello_nolib.pc"} ], "stdout": [ { diff --git a/test cases/common/66 vcstag/meson.build b/test cases/common/66 vcstag/meson.build index 256d2e9..520f72a 100644 --- a/test cases/common/66 vcstag/meson.build +++ b/test cases/common/66 vcstag/meson.build @@ -9,9 +9,15 @@ output : 'vcstag-custom.c', command : ['git', 'show-ref', '-s', 'refs/heads/master'], fallback : '1.0.0') +version_src_notfound_fallback = vcs_tag(input : 'vcstag.c.in', +output : 'vcstag-notfound-fallback.c', +command : ['git-but-not-found-sorry', 'show-ref', '-s', 'refs/heads/master'], +fallback : '1.0.0') + version_src_fallback = vcs_tag(input : 'vcstag.c.in', output : 'vcstag-fallback.c') executable('tagprog', 'tagprog.c', version_src) executable('tagprog-custom', 'tagprog.c', version_src_custom) executable('tagprog-fallback', 'tagprog.c', version_src_fallback) +executable('tagprog-notfound-fallback', 'tagprog.c', version_src_notfound_fallback) diff --git a/test cases/failing/54 wrong shared crate type/test.json b/test cases/failing/54 wrong shared crate type/test.json index 5cced6f..a5b7961 100644 --- a/test cases/failing/54 wrong shared crate type/test.json +++ b/test cases/failing/54 wrong shared crate type/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/54 wrong shared crate type/meson.build:7:0: ERROR: Crate type \"staticlib\" invalid for dynamic libraries; must be \"dylib\" or \"cdylib\"" + "line": "test cases/failing/54 wrong shared crate type/meson.build:7:0: ERROR: Crate type \"staticlib\" invalid for dynamic libraries; must be \"dylib\", \"cdylib\", or \"proc-macro\"" } ] } diff --git a/test cases/java/9 jni/meson.build b/test cases/java/9 jni/meson.build index 1239e19..90a8485 100644 --- a/test cases/java/9 jni/meson.build +++ b/test cases/java/9 jni/meson.build @@ -16,6 +16,17 @@ java = find_program('java') jni_dep = dependency('jni', version : '>=1.8', modules: ['jvm', 'awt']) # generate native headers -subdir('src/com/mesonbuild') -subdir('lib') subdir('src') +subdir('lib') + +test( + 'jnitest', + java, + args: [ + '-Djava.library.path=@0@'.format(fs.parent(jnijava.full_path())), + '-jar', + jnijar, + ], + protocol : 'exitcode', + depends : [jnijava], +) diff --git a/test cases/java/9 jni/src/com/mesonbuild/Configured.java.in b/test cases/java/9 jni/src/com/mesonbuild/Configured.java.in new file mode 100644 index 0000000..fac6e05 --- /dev/null +++ b/test cases/java/9 jni/src/com/mesonbuild/Configured.java.in @@ -0,0 +1,5 @@ +package com.mesonbuild; + +public final class Configured { + public static final int FINGERPRINT = @fingerprint@; +} diff --git a/test cases/java/9 jni/src/com/mesonbuild/JniTest.java b/test cases/java/9 jni/src/com/mesonbuild/JniTest.java index f80b326..4bfffe9 100644 --- a/test cases/java/9 jni/src/com/mesonbuild/JniTest.java +++ b/test cases/java/9 jni/src/com/mesonbuild/JniTest.java @@ -4,7 +4,7 @@ public final class JniTest { private static native int jni_test(); public static void main(String[] args) { - if (jni_test() != 0xdeadbeef) { + if (jni_test() != Configured.FINGERPRINT) { throw new RuntimeException("jdk_test() did not return 0"); } } diff --git a/test cases/java/9 jni/src/com/mesonbuild/meson.build b/test cases/java/9 jni/src/com/mesonbuild/meson.build index 3ee2083..2d88c5e 100644 --- a/test cases/java/9 jni/src/com/mesonbuild/meson.build +++ b/test cases/java/9 jni/src/com/mesonbuild/meson.build @@ -1,3 +1,11 @@ +configured = configure_file( + input: files('Configured.java.in'), + output: 'Configured.java', + configuration: configuration_data({'fingerprint': '0xdeadbeef'}) +) + +sources += configured + native_headers = javamod.generate_native_headers( - 'JniTest.java', package: 'com.mesonbuild', classes: ['JdkTest']) + sources, package: 'com.mesonbuild', classes: ['JniTest']) native_header_includes = include_directories('.') diff --git a/test cases/java/9 jni/src/meson.build b/test cases/java/9 jni/src/meson.build index 07a0664..af443b5 100644 --- a/test cases/java/9 jni/src/meson.build +++ b/test cases/java/9 jni/src/meson.build @@ -1,17 +1,9 @@ +sources = [files('com/mesonbuild/JniTest.java')] + +subdir('com/mesonbuild') + jnijar = jar( 'jnijar', - 'com' / 'mesonbuild' / 'JniTest.java', + sources, main_class : 'com.mesonbuild.JniTest', ) - -test( - 'jnitest', - java, - args: [ - '-Djava.library.path=@0@'.format(fs.parent(jnijava.full_path())), - '-jar', - jnijar, - ], - protocol : 'exitcode', - depends : [jnijava], -) diff --git a/test cases/osx/7 bitcode/meson.build b/test cases/osx/7 bitcode/meson.build index f94bf9d..50ce3f2 100644 --- a/test cases/osx/7 bitcode/meson.build +++ b/test cases/osx/7 bitcode/meson.build @@ -1,4 +1,5 @@ -project('bitcode test', 'c', 'objc', 'objcpp') +project('bitcode test', 'c', 'objc', 'objcpp', + default_options : ['b_bitcode=true']) both_libraries('alib', 'libfoo.m') shared_module('amodule', 'libfoo.m') diff --git a/test cases/rust/18 proc-macro/meson.build b/test cases/rust/18 proc-macro/meson.build new file mode 100644 index 0000000..01c4cbe --- /dev/null +++ b/test cases/rust/18 proc-macro/meson.build @@ -0,0 +1,19 @@ +project('rust proc-macro', 'rust') + +if build_machine.system() != 'linux' + error('MESON_SKIP_TEST, this test only works on Linux. Patches welcome.') +endif + +pm = shared_library( + 'proc_macro_examples', + 'proc.rs', + rust_crate_type : 'proc-macro', +) + +main = executable( + 'main', + 'use.rs', + link_with : pm +) + +test('main_test', main) diff --git a/test cases/rust/18 proc-macro/proc.rs b/test cases/rust/18 proc-macro/proc.rs new file mode 100644 index 0000000..53935e4 --- /dev/null +++ b/test cases/rust/18 proc-macro/proc.rs @@ -0,0 +1,7 @@ +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +pub fn make_answer(_item: TokenStream) -> TokenStream { + "fn answer() -> u32 { 42 }".parse().unwrap() +} diff --git a/test cases/rust/18 proc-macro/use.rs b/test cases/rust/18 proc-macro/use.rs new file mode 100644 index 0000000..0b6342b --- /dev/null +++ b/test cases/rust/18 proc-macro/use.rs @@ -0,0 +1,8 @@ +extern crate proc_macro_examples; +use proc_macro_examples::make_answer; + +make_answer!(); + +fn main() { + assert_eq!(42, answer()); +} diff --git a/test cases/unit/104 strip/lib.c b/test cases/unit/104 strip/lib.c new file mode 100644 index 0000000..3940fde --- /dev/null +++ b/test cases/unit/104 strip/lib.c @@ -0,0 +1 @@ +void func(void){} diff --git a/test cases/unit/104 strip/meson.build b/test cases/unit/104 strip/meson.build new file mode 100644 index 0000000..dff61ab --- /dev/null +++ b/test cases/unit/104 strip/meson.build @@ -0,0 +1,3 @@ +project('strip', 'c') + +shared_library('a', 'lib.c', install: true) diff --git a/tools/regenerate_docs.py b/tools/regenerate_docs.py index 887db9c..25e6b53 100755 --- a/tools/regenerate_docs.py +++ b/tools/regenerate_docs.py @@ -37,6 +37,23 @@ def _get_meson_output(root_dir: Path, args: T.List) -> str: env['COLUMNS'] = '80' return subprocess.run([str(sys.executable), str(root_dir/'meson.py')] + args, check=True, capture_output=True, text=True, env=env).stdout.strip() +def get_commands(help_output: str) -> T.Set[str]: + # Python's argument parser might put the command list to its own line. Or it might not. + assert(help_output.startswith('usage: ')) + lines = help_output.split('\n') + line1 = lines[0] + line2 = lines[1] + if '{' in line1: + cmndline = line1 + else: + assert('{' in line2) + cmndline = line2 + cmndstr = cmndline.split('{')[1] + assert('}' in cmndstr) + help_commands = set(cmndstr.split('}')[0].split(',')) + assert(len(help_commands) > 0) + return {c.strip() for c in help_commands} + def get_commands_data(root_dir: Path) -> T.Dict[str, T.Any]: usage_start_pattern = re.compile(r'^usage: ', re.MULTILINE) positional_start_pattern = re.compile(r'^positional arguments:[\t ]*[\r\n]+', re.MULTILINE) @@ -96,7 +113,7 @@ def get_commands_data(root_dir: Path) -> T.Dict[str, T.Any]: return out output = _get_meson_output(root_dir, ['--help']) - commands = {c.strip() for c in re.findall(r'usage:(?:.+)?{((?:[a-z]+,*)+?)}', output, re.MULTILINE|re.DOTALL)[0].split(',')} + commands = get_commands(output) commands.remove('help') cmd_data = dict() diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index b424b15..5a533a9 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -3376,9 +3376,21 @@ class AllPlatformTests(BasePlatformTests): ## Validate commands md_commands = {k for k,v in md_command_sections.items()} - help_output = self._run(self.meson_command + ['--help']) - help_commands = {c.strip() for c in re.findall(r'usage:(?:.+)?{((?:[a-z]+,*)+?)}', help_output, re.MULTILINE|re.DOTALL)[0].split(',')} + # Python's argument parser might put the command list to its own line. Or it might not. + self.assertTrue(help_output.startswith('usage: ')) + lines = help_output.split('\n') + line1 = lines[0] + line2 = lines[1] + if '{' in line1: + cmndline = line1 + else: + self.assertIn('{', line2) + cmndline = line2 + cmndstr = cmndline.split('{')[1] + self.assertIn('}', cmndstr) + help_commands = set(cmndstr.split('}')[0].split(',')) + self.assertTrue(len(help_commands) > 0, 'Must detect some command names.') self.assertEqual(md_commands | {'help'}, help_commands, f'Doc file: `{doc_path}`') diff --git a/unittests/linuxliketests.py b/unittests/linuxliketests.py index f3dc7b3..79db0b8 100644 --- a/unittests/linuxliketests.py +++ b/unittests/linuxliketests.py @@ -1760,3 +1760,23 @@ class LinuxlikeTests(BasePlatformTests): # If so, we can test that cmake works with "gcc -m32" self.do_one_test_with_nativefile('../cmake/1 basic', "['gcc', '-m32']") + + @skipUnless(is_linux(), 'Test only applicable to Linux') + def test_install_strip(self): + testdir = os.path.join(self.unit_test_dir, '104 strip') + self.init(testdir) + self.build() + + destdir = self.installdir + self.prefix + lib = os.path.join(destdir, self.libdir, 'liba.so') + install_cmd = self.meson_command + ['install', '--destdir', self.installdir] + + # Check we have debug symbols by default + self._run(install_cmd, workdir=self.builddir) + stdout = self._run(['file', '-b', lib]) + self.assertIn('not stripped', stdout) + + # Check debug symbols got removed with --strip + self._run(install_cmd + ['--strip'], workdir=self.builddir) + stdout = self._run(['file', '-b', lib]) + self.assertNotIn('not stripped', stdout) |