| # Copyright 2019 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. |
| |
| from collections import namedtuple |
| from .. import mesonlib |
| from ..mesonlib import listify, OrderedSet |
| from . import ExtensionModule |
| from ..interpreterbase import ( |
| noPosargs, noKwargs, permittedKwargs, |
| InterpreterObject, MutableInterpreterObject, ObjectHolder, |
| InterpreterException, InvalidArguments, InvalidCode, FeatureNew, |
| ) |
| from ..interpreter import ( |
| GeneratedListHolder, CustomTargetHolder, |
| CustomTargetIndexHolder |
| ) |
| |
| SourceSetRule = namedtuple('SourceSetRule', 'keys sources if_false sourcesets dependencies extra_deps') |
| SourceFiles = namedtuple('SourceFiles', 'sources dependencies') |
| |
| class SourceSetHolder(MutableInterpreterObject, ObjectHolder): |
| def __init__(self, interpreter): |
| MutableInterpreterObject.__init__(self) |
| ObjectHolder.__init__(self, list()) |
| self.subproject = interpreter.subproject |
| self.environment = interpreter.environment |
| self.subdir = interpreter.subdir |
| self.frozen = False |
| self.methods.update({ |
| 'add': self.add_method, |
| 'add_all': self.add_all_method, |
| 'all_sources': self.all_sources_method, |
| 'all_dependencies': self.all_dependencies_method, |
| 'apply': self.apply_method, |
| }) |
| |
| def check_source_files(self, arg, allow_deps): |
| sources = [] |
| deps = [] |
| for x in arg: |
| if isinstance(x, (str, mesonlib.File, |
| GeneratedListHolder, CustomTargetHolder, |
| CustomTargetIndexHolder)): |
| sources.append(x) |
| elif hasattr(x, 'found'): |
| if not allow_deps: |
| msg = 'Dependencies are not allowed in the if_false argument.' |
| raise InvalidArguments(msg) |
| deps.append(x) |
| else: |
| msg = 'Sources must be strings or file-like objects.' |
| raise InvalidArguments(msg) |
| mesonlib.check_direntry_issues(sources) |
| return sources, deps |
| |
| def check_conditions(self, arg): |
| keys = [] |
| deps = [] |
| for x in listify(arg): |
| if isinstance(x, str): |
| keys.append(x) |
| elif hasattr(x, 'found'): |
| deps.append(x) |
| else: |
| raise InvalidArguments('Conditions must be strings or dependency object') |
| return keys, deps |
| |
| @permittedKwargs(['when', 'if_false', 'if_true']) |
| def add_method(self, args, kwargs): |
| if self.frozen: |
| raise InvalidCode('Tried to use \'add\' after querying the source set') |
| when = listify(kwargs.get('when', [])) |
| if_true = listify(kwargs.get('if_true', [])) |
| if_false = listify(kwargs.get('if_false', [])) |
| if not when and not if_true and not if_false: |
| if_true = args |
| elif args: |
| raise InterpreterException('add called with both positional and keyword arguments') |
| keys, dependencies = self.check_conditions(when) |
| sources, extra_deps = self.check_source_files(if_true, True) |
| if_false, _ = self.check_source_files(if_false, False) |
| self.held_object.append(SourceSetRule(keys, sources, if_false, [], dependencies, extra_deps)) |
| |
| @permittedKwargs(['when', 'if_true']) |
| def add_all_method(self, args, kwargs): |
| if self.frozen: |
| raise InvalidCode('Tried to use \'add_all\' after querying the source set') |
| when = listify(kwargs.get('when', [])) |
| if_true = listify(kwargs.get('if_true', [])) |
| if not when and not if_true: |
| if_true = args |
| elif args: |
| raise InterpreterException('add_all called with both positional and keyword arguments') |
| keys, dependencies = self.check_conditions(when) |
| for s in if_true: |
| if not isinstance(s, SourceSetHolder): |
| raise InvalidCode('Arguments to \'add_all\' after the first must be source sets') |
| s.frozen = True |
| self.held_object.append(SourceSetRule(keys, [], [], if_true, dependencies, [])) |
| |
| def collect(self, enabled_fn, all_sources, into=None): |
| if not into: |
| into = SourceFiles(OrderedSet(), OrderedSet()) |
| for entry in self.held_object: |
| if all(x.found() for x in entry.dependencies) and \ |
| all(enabled_fn(key) for key in entry.keys): |
| into.sources.update(entry.sources) |
| into.dependencies.update(entry.dependencies) |
| into.dependencies.update(entry.extra_deps) |
| for ss in entry.sourcesets: |
| ss.collect(enabled_fn, all_sources, into) |
| if not all_sources: |
| continue |
| into.sources.update(entry.if_false) |
| return into |
| |
| @noKwargs |
| @noPosargs |
| def all_sources_method(self, args, kwargs): |
| self.frozen = True |
| files = self.collect(lambda x: True, True) |
| return list(files.sources) |
| |
| @noKwargs |
| @noPosargs |
| @FeatureNew('source_set.all_dependencies() method', '0.52.0') |
| def all_dependencies_method(self, args, kwargs): |
| self.frozen = True |
| files = self.collect(lambda x: True, True) |
| return list(files.dependencies) |
| |
| @permittedKwargs(['strict']) |
| def apply_method(self, args, kwargs): |
| if len(args) != 1: |
| raise InterpreterException('Apply takes exactly one argument') |
| config_data = args[0] |
| self.frozen = True |
| strict = kwargs.get('strict', True) |
| if isinstance(config_data, dict): |
| def _get_from_config_data(key): |
| if strict and key not in config_data: |
| raise InterpreterException(f'Entry {key} not in configuration dictionary.') |
| return config_data.get(key, False) |
| else: |
| config_cache = dict() |
| |
| def _get_from_config_data(key): |
| nonlocal config_cache |
| if key not in config_cache: |
| args = [key] if strict else [key, False] |
| config_cache[key] = config_data.get_method(args, {}) |
| return config_cache[key] |
| |
| files = self.collect(_get_from_config_data, False) |
| res = SourceFilesHolder(files) |
| return res |
| |
| class SourceFilesHolder(InterpreterObject, ObjectHolder): |
| def __init__(self, files): |
| InterpreterObject.__init__(self) |
| ObjectHolder.__init__(self, files) |
| self.methods.update({ |
| 'sources': self.sources_method, |
| 'dependencies': self.dependencies_method, |
| }) |
| |
| @noPosargs |
| @noKwargs |
| def sources_method(self, args, kwargs): |
| return list(self.held_object.sources) |
| |
| @noPosargs |
| @noKwargs |
| def dependencies_method(self, args, kwargs): |
| return list(self.held_object.dependencies) |
| |
| class SourceSetModule(ExtensionModule): |
| @FeatureNew('SourceSet module', '0.51.0') |
| def __init__(self, *args, **kwargs): |
| super().__init__(*args, **kwargs) |
| self.snippets.add('source_set') |
| |
| @noKwargs |
| @noPosargs |
| def source_set(self, interpreter, state, args, kwargs): |
| return SourceSetHolder(interpreter) |
| |
| def initialize(*args, **kwargs): |
| return SourceSetModule(*args, **kwargs) |