sanitycheck: Add functions to query device tree for filters

Add the following functions to allow filtering based on device tree

dt_compat_enabled(compat) - Returns true if a device tree node
compatible matches 'compat' and the node is enabled.

dt_alias_exists(alias) - Returns true if a device tree node exists with
'alias' and the node is enabled.

dt_compat_enabled_with_alias - Returns true if a device tree node
compatible matches 'compat' and the node has 'alias' and the node is
enabled.

Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
This commit is contained in:
Kumar Gala 2019-09-12 17:08:43 -05:00 committed by Anas Nashif
commit 7733b94224
2 changed files with 51 additions and 13 deletions

View file

@ -134,6 +134,20 @@ def p_expr_single(p):
"""expr : SYMBOL""" """expr : SYMBOL"""
p[0] = ("exists", p[1]) p[0] = ("exists", p[1])
def p_func(p):
"""expr : SYMBOL OPAREN arg_intr CPAREN"""
p[0] = [p[1]]
p[0].append(p[3])
def p_arg_intr_single(p):
"""arg_intr : const"""
p[0] = [p[1]]
def p_arg_intr_mult(p):
"""arg_intr : arg_intr COMMA const"""
p[0] = copy.copy(p[1])
p[0].append(p[3])
def p_list(p): def p_list(p):
"""list : OBRACKET list_intr CBRACKET""" """list : OBRACKET list_intr CBRACKET"""
p[0] = p[2] p[0] = p[2]
@ -182,13 +196,13 @@ def ast_sym_int(ast, env):
return int(v, 10) return int(v, 10)
return 0 return 0
def ast_expr(ast, env): def ast_expr(ast, env, edt):
if ast[0] == "not": if ast[0] == "not":
return not ast_expr(ast[1], env) return not ast_expr(ast[1], env, edt)
elif ast[0] == "or": elif ast[0] == "or":
return ast_expr(ast[1], env) or ast_expr(ast[2], env) return ast_expr(ast[1], env, edt) or ast_expr(ast[2], env, edt)
elif ast[0] == "and": elif ast[0] == "and":
return ast_expr(ast[1], env) and ast_expr(ast[2], env) return ast_expr(ast[1], env, edt) and ast_expr(ast[2], env, edt)
elif ast[0] == "==": elif ast[0] == "==":
return ast_sym(ast[1], env) == ast[2] return ast_sym(ast[1], env) == ast[2]
elif ast[0] == "!=": elif ast[0] == "!=":
@ -207,10 +221,29 @@ def ast_expr(ast, env):
return bool(ast_sym(ast[1], env)) return bool(ast_sym(ast[1], env))
elif ast[0] == ":": elif ast[0] == ":":
return bool(re.match(ast[2], ast_sym(ast[1], env))) return bool(re.match(ast[2], ast_sym(ast[1], env)))
elif ast[0] == "dt_compat_enabled":
compat = ast[1][0]
for node in edt.nodes:
if compat in node.compats and node.enabled:
return True
return False
elif ast[0] == "dt_alias_exists":
alias = ast[1][0]
for node in edt.nodes:
if alias in node.aliases and node.enabled:
return True
return False
elif ast[0] == "dt_compat_enabled_with_alias":
compat = ast[1][0]
alias = ast[1][1]
for node in edt.nodes:
if node.enabled and alias in node.aliases and node.matching_compat == compat:
return True
return False
mutex = threading.Lock() mutex = threading.Lock()
def parse(expr_text, env): def parse(expr_text, env, edt):
"""Given a text representation of an expression in our language, """Given a text representation of an expression in our language,
use the provided environment to determine whether the expression use the provided environment to determine whether the expression
is true or false""" is true or false"""
@ -222,7 +255,7 @@ def parse(expr_text, env):
finally: finally:
mutex.release() mutex.release()
return ast_expr(ast, env) return ast_expr(ast, env, edt)
# Just some test code # Just some test code
if __name__ == "__main__": if __name__ == "__main__":
@ -244,4 +277,4 @@ if __name__ == "__main__":
parser = yacc.yacc() parser = yacc.yacc()
print(parser.parse(line)) print(parser.parse(line))
print(parse(line, local_env)) print(parse(line, local_env, None))

View file

@ -199,6 +199,13 @@ from itertools import islice
from pathlib import Path from pathlib import Path
from distutils.spawn import find_executable from distutils.spawn import find_executable
ZEPHYR_BASE = os.getenv("ZEPHYR_BASE")
if not ZEPHYR_BASE:
sys.exit("$ZEPHYR_BASE environment variable undefined")
sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts", "dts"))
import edtlib
import logging import logging
@ -209,11 +216,6 @@ report_lock = threading.Lock()
log_format = "%(levelname)s %(name)s::%(module)s.%(funcName)s():%(lineno)d: %(message)s" log_format = "%(levelname)s %(name)s::%(module)s.%(funcName)s():%(lineno)d: %(message)s"
logging.basicConfig(format=log_format, level=30) logging.basicConfig(format=log_format, level=30)
ZEPHYR_BASE = os.environ.get("ZEPHYR_BASE")
if not ZEPHYR_BASE:
sys.stderr.write("$ZEPHYR_BASE environment variable undefined.\n")
exit(1)
# Use this for internal comparisons; that's what canonicalization is # Use this for internal comparisons; that's what canonicalization is
# for. Don't use it when invoking other components of the build system # for. Don't use it when invoking other components of the build system
# to avoid confusing and hard to trace inconsistencies in error messages # to avoid confusing and hard to trace inconsistencies in error messages
@ -1902,7 +1904,10 @@ class FilterBuilder(CMake):
if self.testcase and self.testcase.tc_filter: if self.testcase and self.testcase.tc_filter:
try: try:
res = expr_parser.parse(self.testcase.tc_filter, filter_data) dts_path = os.path.join(self.build_dir, "zephyr", self.platform.name + ".dts.pre.tmp")
edt = edtlib.EDT(dts_path, [os.path.join(ZEPHYR_BASE, "dts", "bindings")])
res = expr_parser.parse(self.testcase.tc_filter, filter_data, edt)
except (ValueError, SyntaxError) as se: except (ValueError, SyntaxError) as se:
sys.stderr.write( sys.stderr.write(
"Failed processing %s\n" % self.testcase.yamlfile) "Failed processing %s\n" % self.testcase.yamlfile)