aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mensinger <daniel@mensinger-ka.de>2021-06-25 21:14:27 +0200
committerDaniel Mensinger <daniel@mensinger-ka.de>2021-06-26 12:49:35 +0200
commit8f7343831bb78e6801c93cd45e4edfeed65bc437 (patch)
treee90a37ac9b27584b8c43a32779330705973def02
parentb95d6e319f18389c3a11408481335955bdff4e36 (diff)
downloadmeson-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.py16
-rw-r--r--mesonbuild/dependencies/base.py11
-rw-r--r--mesonbuild/interpreter/interpreter.py5
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py6
-rw-r--r--mesonbuild/interpreterbase/__init__.py5
-rw-r--r--mesonbuild/interpreterbase/baseobjects.py4
-rw-r--r--mesonbuild/interpreterbase/decorators.py4
-rw-r--r--mesonbuild/interpreterbase/helpers.py13
-rw-r--r--mesonbuild/interpreterbase/interpreterbase.py4
-rw-r--r--mesonbuild/mesonlib/universal.py9
-rw-r--r--mesonbuild/modules/gnome.py2
-rw-r--r--mesonbuild/modules/pkgconfig.py19
-rw-r--r--test cases/common/178 bothlibraries/meson.build8
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)