From acb28e922bec72e5901810d53d7746d9978185f1 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 18 Sep 2017 22:35:37 +0200 Subject: build: use unified dependency expressions instead of weird fields Instead of "deps", "deps_neg", and "deps_any" fields, just have a single "deps" field, which changes from an array to a string. The string is now an expression, which can contain the operators &&, ||, !, and allows grouping with ( ). It's probably overkill. If it gets a maintenance burden, we can switch to specifiying the dep expressions as ASTs (or maybe eval()-able Python expressions), and we could simplify the code that determines the reason why a dependency is not fulfilled. The latter involves a complicated conversion of the expression AST to DNF. The parser is actually pretty simple, and pretty much follows: https://en.wikipedia.org/wiki/Shunting_yard_algorithm --- waftools/dependencies.py | 64 ++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 40 deletions(-) (limited to 'waftools/dependencies.py') diff --git a/waftools/dependencies.py b/waftools/dependencies.py index 994e1d10b2..3cef6a3562 100644 --- a/waftools/dependencies.py +++ b/waftools/dependencies.py @@ -2,6 +2,7 @@ from waflib.Errors import ConfigurationError, WafError from waflib.Configure import conf from waflib.Build import BuildContext from waflib.Logs import pprint +import deps_parser import inflector class DependencyError(Exception): @@ -16,11 +17,9 @@ class Dependency(object): self.attributes = self.__parse_attributes__(dependency) known_deps.add(self.identifier) - for dep_key in ['deps', 'deps_any', 'deps_neg']: - if dep_key in self.attributes: - deps = self.attributes[dep_key] - self.ctx.ensure_dependency_is_known(*deps) + if 'deps' in self.attributes: + self.ctx.ensure_dependency_is_known(self.attributes['deps']) def __parse_attributes__(self, dependency): if 'os_specific_checks' in dependency: @@ -36,9 +35,7 @@ class Dependency(object): try: self.check_group_disabled() self.check_disabled() - self.check_any_dependencies() self.check_dependencies() - self.check_negative_dependencies() except DependencyError: # No check was run, since the prerequisites of the dependency are # not satisfied. Make sure the define is 'undefined' so that we @@ -67,27 +64,12 @@ class Dependency(object): self.attributes['fmsg'] = "You manually enabled the feature '{0}', but \ the autodetection check failed.".format(self.identifier) - def check_any_dependencies(self): - if 'deps_any' in self.attributes: - deps = set(self.attributes['deps_any']) - if len(deps & self.satisfied_deps) == 0: - self.skip("not found any of {0}".format(", ".join(deps))) - raise DependencyError - def check_dependencies(self): if 'deps' in self.attributes: - deps = set(self.attributes['deps']) - if not deps <= self.satisfied_deps: - missing_deps = deps - self.satisfied_deps - self.skip("{0} not found".format(", ".join(missing_deps))) - raise DependencyError - - def check_negative_dependencies(self): - if 'deps_neg' in self.attributes: - deps = set(self.attributes['deps_neg']) - conflicting_deps = deps & self.satisfied_deps - if len(conflicting_deps) > 0: - self.skip("{0} found".format(", ".join(conflicting_deps)), 'CYAN') + ok, why = deps_parser.check_dependency_expr(self.attributes['deps'], + self.satisfied_deps) + if not ok: + self.skip(why) raise DependencyError def check_autodetect_func(self): @@ -145,13 +127,19 @@ def configure(ctx): __detect_target_os_dependency__(ctx) @conf -def ensure_dependency_is_known(ctx, *depnames): - deps = set([d for d in depnames if not d.startswith('os-')]) - if not deps <= ctx.known_deps: - raise ConfigurationError( - "error in dependencies definition: some dependencies in" - " {0} are unknown.".format(deps)) - +def ensure_dependency_is_known(ctx, depnames): + def check(ast): + if isinstance(ast, deps_parser.AstSym): + if (not ast.name.startswith('os-')) and ast.name not in ctx.known_deps: + raise ConfigurationError( + "error in dependencies definition: dependency {0} in" + " {1} is unknown.".format(ast.name, depnames)) + elif isinstance(ast, deps_parser.AstOp): + for sub in ast.sub: + check(sub) + else: + assert False + check(deps_parser.parse_expr(depnames)) @conf def mark_satisfied(ctx, dependency_identifier): @@ -174,7 +162,9 @@ def parse_dependencies(ctx, dependencies): @conf def dependency_satisfied(ctx, dependency_identifier): ctx.ensure_dependency_is_known(dependency_identifier) - return dependency_identifier in ctx.satisfied_deps + ok, _ = deps_parser.check_dependency_expr(dependency_identifier, + ctx.satisfied_deps) + return ok @conf def store_dependencies_lists(ctx): @@ -194,13 +184,7 @@ def filtered_sources(ctx, sources): return source def __check_filter__(dependency): - if dependency.find('!') == 0: - dependency = dependency.lstrip('!') - ctx.ensure_dependency_is_known(dependency) - return dependency not in ctx.satisfied_deps - else: - ctx.ensure_dependency_is_known(dependency) - return dependency in ctx.satisfied_deps + return dependency_satisfied(ctx, dependency) def __unpack_and_check_filter__(source): try: -- cgit v1.2.3