diff options
author | Daniel Mensinger <daniel@mensinger-ka.de> | 2021-06-25 21:14:27 +0200 |
---|---|---|
committer | Daniel Mensinger <daniel@mensinger-ka.de> | 2021-06-26 12:49:35 +0200 |
commit | 8f7343831bb78e6801c93cd45e4edfeed65bc437 (patch) | |
tree | e90a37ac9b27584b8c43a32779330705973def02 | |
parent | b95d6e319f18389c3a11408481335955bdff4e36 (diff) | |
download | meson-8f7343831bb78e6801c93cd45e4edfeed65bc437.zip meson-8f7343831bb78e6801c93cd45e4edfeed65bc437.tar.gz meson-8f7343831bb78e6801c93cd45e4edfeed65bc437.tar.bz2 |
refactor: Refactor BothLibraries logic
This commit introduces a new type of `HoldableObject`: The
`SecondLevelHolder`. The primary purpose of this class is
to handle cases where two (or more) `HoldableObject`s are
stored at the same time (with one default object). The
best (and currently only) example here is the `BothLibraries`
class.
-rw-r--r-- | mesonbuild/build.py | 16 | ||||
-rw-r--r-- | mesonbuild/dependencies/base.py | 11 | ||||
-rw-r--r-- | mesonbuild/interpreter/interpreter.py | 5 | ||||
-rw-r--r-- | mesonbuild/interpreter/interpreterobjects.py | 6 | ||||
-rw-r--r-- | mesonbuild/interpreterbase/__init__.py | 5 | ||||
-rw-r--r-- | mesonbuild/interpreterbase/baseobjects.py | 4 | ||||
-rw-r--r-- | mesonbuild/interpreterbase/decorators.py | 4 | ||||
-rw-r--r-- | mesonbuild/interpreterbase/helpers.py | 13 | ||||
-rw-r--r-- | mesonbuild/interpreterbase/interpreterbase.py | 4 | ||||
-rw-r--r-- | mesonbuild/mesonlib/universal.py | 9 | ||||
-rw-r--r-- | mesonbuild/modules/gnome.py | 2 | ||||
-rw-r--r-- | mesonbuild/modules/pkgconfig.py | 19 | ||||
-rw-r--r-- | test cases/common/178 bothlibraries/meson.build | 8 |
13 files changed, 56 insertions, 50 deletions
diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 25c8ca1..f9de5c4 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -28,7 +28,7 @@ from . import dependencies from . import mlog from . import programs from .mesonlib import ( - HoldableObject, + HoldableObject, SecondLevelHolder, File, MesonException, MachineChoice, PerMachine, OrderedSet, listify, extract_as_list, typeslistify, stringlistify, classify_unity_sources, get_filenames_templates_dict, substitute_values, has_path_sep, @@ -1234,8 +1234,6 @@ You probably should put it in link_with instead.''') def link(self, target): for t in listify(target): - if isinstance(t, BothLibraries): - t = t.get_preferred_library() if isinstance(self, StaticLibrary) and self.need_install: if isinstance(t, (CustomTarget, CustomTargetIndex)): if not t.should_install(): @@ -1264,9 +1262,6 @@ You probably should put it in link_with instead.''') def link_whole(self, target): for t in listify(target): - # Always use the static library from BothLibraries, since shared libs aren't supported anyway - if isinstance(t, BothLibraries): - t = t.static if isinstance(t, (CustomTarget, CustomTargetIndex)): if not t.is_linkable_target(): raise InvalidArguments(f'Custom target {t!r} is not linkable.') @@ -2152,7 +2147,7 @@ class SharedModule(SharedLibrary): def get_default_install_dir(self, environment): return environment.get_shared_module_dir() -class BothLibraries(HoldableObject): +class BothLibraries(SecondLevelHolder): def __init__(self, shared: SharedLibrary, static: StaticLibrary) -> None: self._preferred_library = 'shared' self.shared = shared @@ -2162,7 +2157,7 @@ class BothLibraries(HoldableObject): def __repr__(self) -> str: return f'<BothLibraries: static={repr(self.static)}; shared={repr(self.shared)}>' - def get_preferred_library(self) -> BuildTarget: + def get_default_object(self) -> BuildTarget: if self._preferred_library == 'shared': return self.shared elif self._preferred_library == 'static': @@ -2174,8 +2169,6 @@ class CommandBase: cmd = listify(cmd) final_cmd = [] for c in cmd: - if isinstance(c, BothLibraries): - c = c.get_preferred_library() if isinstance(c, str): final_cmd.append(c) elif isinstance(c, File): @@ -2274,7 +2267,6 @@ class CustomTarget(Target, CommandBase): def process_kwargs(self, kwargs, backend): self.process_kwargs_base(kwargs) self.sources = extract_as_list(kwargs, 'input') - self.sources = [x.get_preferred_library() if isinstance(x, BothLibraries) else x for x in self.sources] if 'output' not in kwargs: raise InvalidArguments('Missing keyword argument "output".') self.outputs = listify(kwargs['output']) @@ -2651,8 +2643,6 @@ def get_sources_string_names(sources, backend): ''' names = [] for s in sources: - if isinstance(s, BothLibraries): - s = s.get_preferred_library() if isinstance(s, str): names.append(s) elif isinstance(s, (BuildTarget, CustomTarget, CustomTargetIndex, GeneratedList)): diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 7240a81..e12c697 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -29,7 +29,7 @@ from ..interpreterbase import FeatureDeprecated if T.TYPE_CHECKING: from ..compilers.compilers import Compiler from ..environment import Environment - from ..build import BuildTarget, BothLibraries + from ..build import BuildTarget from ..mesonlib import FileOrString @@ -222,8 +222,8 @@ class Dependency(HoldableObject): class InternalDependency(Dependency): def __init__(self, version: str, incdirs: T.List[str], compile_args: T.List[str], - link_args: T.List[str], libraries: T.List[T.Union['BuildTarget', 'BothLibraries']], - whole_libraries: T.List[T.Union['BuildTarget', 'BothLibraries']], sources: T.List['FileOrString'], + link_args: T.List[str], libraries: T.List['BuildTarget'], + whole_libraries: T.List['BuildTarget'], sources: T.List['FileOrString'], ext_deps: T.List[Dependency], variables: T.Dict[str, T.Any]): super().__init__(DependencyTypeName('internal'), {}) self.version = version @@ -237,11 +237,6 @@ class InternalDependency(Dependency): self.ext_deps = ext_deps self.variables = variables - # Deal with BothLibraries - from ..build import BothLibraries - self.libraries = [x.get_preferred_library() if isinstance(x, BothLibraries) else x for x in self.libraries] - self.whole_libraries = [x.static if isinstance(x, BothLibraries) else x for x in self.whole_libraries] - def __deepcopy__(self, memo: T.Dict[int, 'InternalDependency']) -> 'InternalDependency': result = self.__class__.__new__(self.__class__) assert isinstance(result, InternalDependency) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d9ecd25..e4fb0d4 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -26,7 +26,7 @@ from ..programs import ExternalProgram, NonExistingExternalProgram from ..dependencies import Dependency from ..depfile import DepFile from ..interpreterbase import ContainerTypeInfo, InterpreterBase, KwargInfo, typed_kwargs, typed_pos_args -from ..interpreterbase import noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening, unholder_return +from ..interpreterbase import noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening, noSecondLevelHolderResolving, unholder_return from ..interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest from ..interpreterbase import Disabler, disablerIfNotFound from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs @@ -2537,8 +2537,6 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey sources = [sources] results: T.List['SourceOutputs'] = [] for s in sources: - if isinstance(s, build.BothLibraries): - s = s.get_preferred_library() if isinstance(s, str): self.validate_within_subproject(self.subdir, s) results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)) @@ -2713,6 +2711,7 @@ This will become a hard error in the future.''', location=self.current_node) @noKwargs @noArgsFlattening + @noSecondLevelHolderResolving def func_set_variable(self, node, args, kwargs): if len(args) != 2: raise InvalidCode('Set_variable takes two arguments.') diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 261d781..03725b9 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -19,7 +19,7 @@ from ..interpreterbase import ( FeatureCheckBase, FeatureNewKwargs, FeatureNew, FeatureDeprecated, typed_pos_args, typed_kwargs, KwargInfo, stringArgs, permittedKwargs, noArgsFlattening, noPosargs, noKwargs, unholder_return, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs, - flatten, InterpreterException, InvalidArguments, InvalidCode) + flatten, resolve_second_level_holders, InterpreterException, InvalidArguments, InvalidCode) from ..dependencies import Dependency, ExternalLibrary, InternalDependency from ..programs import ExternalProgram from ..mesonlib import HoldableObject, MesonException, OptionKey, listify, Popen_safe @@ -754,6 +754,8 @@ class ModuleObjectHolder(ObjectHolder[ModuleObject]): raise InvalidCode(f'Unknown method {method_name!r} in object.') if not getattr(method, 'no-args-flattening', False): args = flatten(args) + if not getattr(method, 'no-second-level-holder-flattening', False): + args, kwargs = resolve_second_level_holders(args, kwargs) state = ModuleState(self.interpreter) # Many modules do for example self.interpreter.find_program_impl(), # so we have to ensure they use the current interpreter and not the one @@ -798,7 +800,7 @@ class BuildTargetHolder(ObjectHolder[_BuildTarget]): @property def _target_object(self) -> build.BuildTarget: if isinstance(self.held_object, build.BothLibraries): - return self.held_object.get_preferred_library() + return self.held_object.get_default_object() assert isinstance(self.held_object, build.BuildTarget) return self.held_object diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py index d776ae1..298a034 100644 --- a/mesonbuild/interpreterbase/__init__.py +++ b/mesonbuild/interpreterbase/__init__.py @@ -33,12 +33,14 @@ __all__ = [ 'check_stringlist', 'default_resolve_key', 'flatten', + 'resolve_second_level_holders', 'noPosargs', 'builtinMethodNoKwargs', 'noKwargs', 'stringArgs', 'noArgsFlattening', + 'noSecondLevelHolderResolving', 'unholder_return', 'disablerIfNotFound', 'permittedKwargs', @@ -91,6 +93,7 @@ from .decorators import ( noKwargs, stringArgs, noArgsFlattening, + noSecondLevelHolderResolving, unholder_return, disablerIfNotFound, permittedKwargs, @@ -115,5 +118,5 @@ from .exceptions import ( ) from .disabler import Disabler, is_disabled -from .helpers import check_stringlist, default_resolve_key, flatten +from .helpers import check_stringlist, default_resolve_key, flatten, resolve_second_level_holders from .interpreterbase import MesonVersionString, InterpreterBase diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index d82aad2..e65e4cb 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -14,7 +14,7 @@ from .. import mparser from .exceptions import InvalidCode -from .helpers import flatten +from .helpers import flatten, resolve_second_level_holders from ..mesonlib import HoldableObject import typing as T @@ -57,6 +57,8 @@ class InterpreterObject: method = self.methods[method_name] if not getattr(method, 'no-args-flattening', False): args = flatten(args) + if not getattr(method, 'no-second-level-holder-flattening', False): + args, kwargs = resolve_second_level_holders(args, kwargs) return method(args, kwargs) raise InvalidCode(f'Unknown method "{method_name}" in object {self} of type {type(self).__name__}.') diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py index b05df5b..b91e9d5 100644 --- a/mesonbuild/interpreterbase/decorators.py +++ b/mesonbuild/interpreterbase/decorators.py @@ -68,6 +68,10 @@ def noArgsFlattening(f: TV_func) -> TV_func: setattr(f, 'no-args-flattening', True) # noqa: B010 return f +def noSecondLevelHolderResolving(f: TV_func) -> TV_func: + setattr(f, 'no-second-level-holder-flattening', True) # noqa: B010 + return f + def unholder_return(f: TV_func) -> T.Callable[..., TYPE_var]: @wraps(f) def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any: diff --git a/mesonbuild/interpreterbase/helpers.py b/mesonbuild/interpreterbase/helpers.py index 2602e80..2352577 100644 --- a/mesonbuild/interpreterbase/helpers.py +++ b/mesonbuild/interpreterbase/helpers.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .. import mparser, mlog +from .. import mesonlib, mparser, mlog from .exceptions import InvalidArguments, InterpreterException import collections.abc @@ -38,6 +38,17 @@ def flatten(args: T.Union['TYPE_var', T.List['TYPE_var']]) -> T.List['TYPE_var'] result.append(a) return result +def resolve_second_level_holders(args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> T.Tuple[T.List['TYPE_var'], 'TYPE_kwargs']: + def resolver(arg: 'TYPE_var') -> 'TYPE_var': + if isinstance(arg, list): + return [resolver(x) for x in arg] + if isinstance(arg, dict): + return {k: resolver(v) for k, v in arg.items()} + if isinstance(arg, mesonlib.SecondLevelHolder): + return arg.get_default_object() + return arg + return [resolver(x) for x in args], {k: resolver(v) for k, v in kwargs.items()} + def check_stringlist(a: T.Any, msg: str = 'Arguments must be strings.') -> None: if not isinstance(a, list): mlog.debug('Not a list:', str(a)) diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py index 3ea10f4..3f5726b 100644 --- a/mesonbuild/interpreterbase/interpreterbase.py +++ b/mesonbuild/interpreterbase/interpreterbase.py @@ -42,7 +42,7 @@ from .exceptions import ( from .decorators import FeatureNew, builtinMethodNoKwargs from .disabler import Disabler, is_disabled -from .helpers import check_stringlist, default_resolve_key, flatten +from .helpers import check_stringlist, default_resolve_key, flatten, resolve_second_level_holders from ._unholder import _unholder import os, copy, re @@ -546,6 +546,8 @@ The result of this is undefined and will become a hard error in a future Meson r func_args = posargs if not getattr(func, 'no-args-flattening', False): func_args = flatten(posargs) + if not getattr(func, 'no-second-level-holder-flattening', False): + func_args, kwargs = resolve_second_level_holders(func_args, kwargs) res = func(node, func_args, kwargs) return self._holderify(res) else: diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index 4b4eb6e..0ed99a5 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -48,6 +48,7 @@ __all__ = [ 'python_command', 'project_meson_versions', 'HoldableObject', + 'SecondLevelHolder', 'File', 'FileMode', 'GitException', @@ -273,6 +274,14 @@ class HoldableObject(metaclass=abc.ABCMeta): ''' Dummy base class for all objects that can be held by an interpreter.baseobjects.ObjectHolder ''' +class SecondLevelHolder(HoldableObject, metaclass=abc.ABCMeta): + ''' A second level object holder. The primary purpose + of such objects is to hold multiple objects with one + default option. ''' + + @abc.abstractmethod + def get_default_object(self) -> HoldableObject: ... + class FileMode: # The first triad is for owner permissions, the second for group permissions, # and the third for others (everyone else). diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index a8f0cc5..efd1f00 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -494,8 +494,6 @@ class GnomeModule(ExtensionModule): return cflags, internal_ldflags, external_ldflags, external_ldflags_nodedup, gi_includes def _unwrap_gir_target(self, girtarget, state): - if isinstance(girtarget, build.BothLibraries): - girtarget = girtarget.get_preferred_library() if not isinstance(girtarget, (build.Executable, build.SharedLibrary, build.StaticLibrary)): raise MesonException(f'Gir target must be an executable or library but is "{girtarget}" of type {type(girtarget).__name__}') diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index f42c1e6..c6eaedc 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -22,9 +22,7 @@ from .. import mesonlib from .. import mlog from . import ModuleReturnValue from . import ExtensionModule -from ..interpreterbase import permittedKwargs, FeatureNew, FeatureNewKwargs, TYPE_var, TYPE_kwargs - -import typing as T +from ..interpreterbase import permittedKwargs, FeatureNew, FeatureNewKwargs already_warned_objs = set() @@ -112,7 +110,6 @@ class DependenciesHelper: def _process_libs(self, libs, public: bool): libs = mesonlib.listify(libs) - libs = [x.get_preferred_library() if isinstance(x, build.BothLibraries) else x for x in libs] processed_libs = [] processed_reqs = [] processed_cflags = [] @@ -454,19 +451,6 @@ class PkgConfigModule(ExtensionModule): if cflags and not dataonly: ofile.write('Cflags: {}\n'.format(' '.join(cflags))) - - @staticmethod - def _handle_both_libraries(args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Tuple[T.List[TYPE_var], TYPE_kwargs]: - def _do_extract(arg: TYPE_var) -> TYPE_var: - if isinstance(arg, list): - return [_do_extract(x) for x in arg] - elif isinstance(arg, dict): - return {k: _do_extract(v) for k, v in arg.items()} - elif isinstance(arg, build.BothLibraries): - return arg.get_preferred_library() - return arg - return [_do_extract(x) for x in args], {k: _do_extract(v) for k, v in kwargs.items()} - @FeatureNewKwargs('pkgconfig.generate', '0.59.0', ['unescaped_variables', 'unescaped_uninstalled_variables']) @FeatureNewKwargs('pkgconfig.generate', '0.54.0', ['uninstalled_variables']) @FeatureNewKwargs('pkgconfig.generate', '0.42.0', ['extra_cflags']) @@ -484,7 +468,6 @@ class PkgConfigModule(ExtensionModule): default_name = None mainlib = None default_subdirs = ['.'] - args, kwargs = PkgConfigModule._handle_both_libraries(args, kwargs) if not args and 'version' not in kwargs: FeatureNew.single_use('pkgconfig.generate implicit version keyword', '0.46.0', state.subproject) elif len(args) == 1: diff --git a/test cases/common/178 bothlibraries/meson.build b/test cases/common/178 bothlibraries/meson.build index 9df4cd1..f3191cc 100644 --- a/test cases/common/178 bothlibraries/meson.build +++ b/test cases/common/178 bothlibraries/meson.build @@ -34,8 +34,16 @@ exe_static2 = executable('prog-static2', 'main.c', link_with : both_libs2.get_static_lib()) exe_both2 = executable('prog-both2', 'main.c', link_with : both_libs2) +# Test {set,get}_variable +set_variable('both_libs2', both_libs) +both_libs3 = get_variable('both_libs') + # Ensure that calling the build target methods also works assert(both_libs.name() == 'mylib') +assert(both_libs2.name() == 'mylib') +assert(both_libs3.name() == 'mylib') +assert(both_libs2.get_shared_lib().name() == 'mylib') +assert(both_libs3.get_static_lib().name() == 'mylib') test('runtest-shared-2', exe_shared2) test('runtest-static-2', exe_static2) |