test_driver.py: Add support for the list-vars-for-cmake option

Signed-off-by: Ronald Cron <[email protected]>
This commit is contained in:
Ronald Cron
2025-12-09 01:04:25 +01:00
parent 9d7330c411
commit 5fecb0e0fa
+97 -21
View File
@@ -12,7 +12,7 @@ import subprocess
from fnmatch import fnmatch
from pathlib import Path
from typing import Iterable, Match, Optional, Set
from typing import Iterable, List, Match, Optional, Set
def get_parsearg_base() -> argparse.ArgumentParser:
""" Get base arguments for scripts building a TF-PSA-Crypto test driver """
@@ -26,6 +26,11 @@ def get_parsearg_base() -> argparse.ArgumentParser:
" - If relative, interpreted relative to the repository root.\n")
parser.add_argument("--driver", default="libtestdriver1", metavar="DRIVER",
help="Test driver name (default: %(default)s).")
parser.add_argument('--list-vars-for-cmake', nargs="?", const="__AUTO__", metavar="FILE",
help="Generate a file to be included from a CMakeLists.txt.\n"
"The file defines CMake list variables with the script's\n"
"inputs/outputs files. If FILE is omitted, the output \n"
"name defaults to '<DRIVER>-list-vars.cmake'.")
return parser
def iter_code_files(root: Path) -> Iterable[Path]:
@@ -38,6 +43,40 @@ def iter_code_files(root: Path) -> Iterable[Path]:
for ext in (".c", ".h"):
yield from directory_path.rglob(f"*{ext}")
def get_src_relpaths(builtin: Path, exclude_files: Set[str]) -> List[Path]:
"""
Return the relative paths of all *.c and *.h files under `builtin`,
excluding those whose names match any of the patterns in `exclude_files`.
The returned paths are relative to `builtin`.
"""
out = []
for file in iter_code_files(builtin):
if not any(fnmatch(file.name, pattern) for pattern in exclude_files):
out.append(file.relative_to(builtin))
out.sort()
return out
def get_dst_relpaths(src_relpaths: List[Path], driver: str) -> List[Path]:
"""
Return the relative paths of the *.c and *.h files generated by the script.
These paths are the same as in `src_relpaths`, except that occurrences of
`mbedtls` in `include/mbedtls/...` paths are replaced with `driver`.
The returned paths are relative to `dst_dir`.
"""
out = []
for path in src_relpaths:
parts = list(path.parts)
if parts[0] == "include" and parts[1] == "mbedtls":
parts[1] = driver
out.append(Path(*parts))
return out
def run_ctags(file: Path) -> Set[str]:
"""
Extract the C identifiers in `file` using ctags.
@@ -71,20 +110,65 @@ def run_ctags(file: Path) -> Set[str]:
class TestDriverGenerator:
"""A TF-PSA-Crypto test driver generator"""
def __init__(self, dst_dir: Path, driver: str):
def __init__(self, src_dir: Path, dst_dir: Path, driver: str, \
exclude_files: Optional[Set[str]] = None) -> None:
"""
Initialize a test driver generator.
Args:
src_dir (Path):
Path to the source directory that contains the built-in driver.
If this path is relative, it should be relative to the repository
root so that the source paths returned by `write_list_vars_for_cmake`
are correct.
The source directory is expected to contain:
- an `include` directory
- an `src` directory
- the `include/` directory contains exactly one subdirectory
dst_dir (Path):
Path to the destination directory where the rewritten tree will
be created.
driver (str):
Name of the driver. This is used as a prefix when rewritting
the tree.
exclude_files (Optional[Set[str]]):
Glob patterns for the basename of the files to be excluded from
the source directory.
"""
self.src_dir = src_dir
self.dst_dir = dst_dir
self.driver = driver
self.exclude_files = set()
if exclude_files is not None:
self.exclude_files = exclude_files
# Path of 'dst_dir'/include/'driver'
self.test_driver_include_dir = None #type: Path | None
def build_tree(self, src_dir: Path, exclude_files: Optional[Set[str]] = None) -> None:
"""
Build a test driver tree from `src_dir`.
if not (src_dir / "include").is_dir():
raise RuntimeError(f'"include" directory in {src_dir} not found')
The source directory `src_dir` is expected to have the following structure:
- an `include` directory
- an `src` directory
- the `include` directory contains exactly one subdirectory
if not (src_dir / "src").is_dir():
raise RuntimeError(f'"src" directory in {src_dir} not found')
def write_list_vars_for_cmake(self, fname: str) -> None:
src_relpaths = get_src_relpaths(self.src_dir, self.exclude_files)
with open(self.dst_dir / fname, "w") as f:
f.write(f"set({self.driver}_input_files " + \
" ".join(str(path) for path in src_relpaths) + ")\n\n")
f.write(f"set({self.driver}_files " + \
" ".join(str(path) \
for path in get_dst_relpaths(src_relpaths, self.driver)) + ")\n\n")
f.write(f"set({self.driver}_src_files " + \
" ".join(str(path) \
for path in src_relpaths if path.suffix == ".c") + ")")
def build_tree(self) -> None:
"""
Build a test driver tree from `self.src_dir`.
Only the `include` and `src` directories from `src_dir` are used to build
the test driver tree, and their directory structure is preserved.
@@ -95,14 +179,9 @@ class TestDriverGenerator:
The subdirectory inside `include` is renamed to `driver` in the test driver
tree, and header inclusions are adjusted accordingly.
"""
include = src_dir / "include"
if not include.is_dir():
raise RuntimeError(f'Do not find "include" directory in {src_dir}')
src = src_dir / "src"
if not src.is_dir():
raise RuntimeError(f'Do not find "src" directory in {src_dir}')
include = self.src_dir / "include"
entries = list(include.iterdir())
if len(entries) != 1 or not entries[0].is_dir():
raise RuntimeError(f"Found more than one directory in {include}")
@@ -115,13 +194,10 @@ class TestDriverGenerator:
if (self.dst_dir / "src").exists():
shutil.rmtree(self.dst_dir / "src")
if exclude_files is None:
exclude_files = set()
for file in iter_code_files(src_dir):
if any(fnmatch(file.name, pattern) for pattern in exclude_files):
for file in iter_code_files(self.src_dir):
if any(fnmatch(file.name, pattern) for pattern in self.exclude_files):
continue
dst = self.dst_dir / file.relative_to(src_dir)
dst = self.dst_dir / file.relative_to(self.src_dir)
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(file, dst)