mirror of
https://github.com/lvgl/lvgl.git
synced 2026-02-06 06:02:10 +08:00
289 lines
8.3 KiB
Python
Executable File
289 lines
8.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import sys
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
|
|
benchmark_configs = {
|
|
"perf32b": {
|
|
"description": "Benchmark config ARM (so3) Emulated - 32 bit",
|
|
"image_name": "ghcr.io/smartobjectoriented/so3-lvperf32b:main",
|
|
"defaults_file": "lv_conf_perf32b.defaults",
|
|
},
|
|
"perf64b": {
|
|
"description": "Benchmark config ARM (so3) Emulated - 64 bit",
|
|
"image_name": "ghcr.io/smartobjectoriented/so3-lvperf64b:main",
|
|
"defaults_file": "lv_conf_perf64b.defaults",
|
|
},
|
|
}
|
|
|
|
lvgl_test_dir = os.path.dirname(os.path.realpath(__file__))
|
|
lvgl_root_dir = os.path.dirname(lvgl_test_dir)
|
|
|
|
|
|
def main() -> int:
|
|
epilog = """This program runs the LVGL demo benchmark
|
|
In order to provide timing consistency between host computers,
|
|
these runs are run in an ARM emulated environment inside QEMU.
|
|
For the runtime environment, SO3 is used which is a lightweight, ARM-based
|
|
operating system.
|
|
Right now, this script requires a host linux computer as we depend on
|
|
`losetup` which is used to set up and control loop devices.
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description="Run LVGL benchmark tests.", epilog=epilog
|
|
)
|
|
parser.add_argument(
|
|
"--config",
|
|
nargs=1,
|
|
choices=benchmark_configs.keys(),
|
|
help="The benchmark config to use. Available configs: "
|
|
+ ", ".join(benchmark_configs.keys()),
|
|
required=False,
|
|
)
|
|
parser.add_argument(
|
|
"--pull",
|
|
action="store_true",
|
|
default=False,
|
|
help="Pull latest images from registry before running tests",
|
|
)
|
|
parser.add_argument(
|
|
"--clean",
|
|
action="store_true",
|
|
default=False,
|
|
help="Clean existing build artifacts before operation",
|
|
)
|
|
parser.add_argument(
|
|
"--auto-clean",
|
|
action="store_true",
|
|
default=False,
|
|
help="Automatically clean build directories",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"actions",
|
|
nargs="*",
|
|
choices=["generate", "run"],
|
|
help="generate: generate the lv_conf.h from the `configs/ci/perf` folder, run: run the benchmark with the provided config",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
if not args.actions:
|
|
print("Error: At least one action must be specified")
|
|
parser.print_help()
|
|
return 1
|
|
|
|
configs = []
|
|
if args.config:
|
|
configs = args.config
|
|
else:
|
|
configs = benchmark_configs.keys()
|
|
|
|
for config in configs:
|
|
if args.clean:
|
|
clean(config)
|
|
|
|
if "generate" in args.actions:
|
|
generate_benchmark_files(config)
|
|
|
|
if "run" in args.actions:
|
|
run_benchmark(config, args.pull)
|
|
|
|
if args.auto_clean:
|
|
clean(config)
|
|
|
|
return 0
|
|
|
|
|
|
def get_build_dir(config_name: str) -> str:
|
|
"""Given the config name, return the build directory path."""
|
|
build_dir_name = f"build_benchmark_{config_name}"
|
|
return os.path.join(lvgl_test_dir, build_dir_name)
|
|
|
|
|
|
def get_container_name(config_name: str) -> str:
|
|
"""
|
|
Returns the docker container name based on the config name
|
|
"""
|
|
return f"lv_benchmark_{config_name}"
|
|
|
|
|
|
def get_docker_volumes(config_name: str) -> list[str]:
|
|
"""
|
|
Returns all docker volume names that should be created
|
|
"""
|
|
return [get_build_cache_volume(config_name), get_disk_cache_volume(config_name)]
|
|
|
|
|
|
def get_build_cache_volume(config_name: str) -> str:
|
|
"""
|
|
Returns the docker volume name for storing cmake generated files
|
|
"""
|
|
return f"{get_container_name(config_name)}_build_cache"
|
|
|
|
|
|
def get_disk_cache_volume(config_name: str) -> str:
|
|
"""
|
|
Returns the docker volume name for storing the virtual disks
|
|
"""
|
|
return f"{get_container_name(config_name)}_disk_cache"
|
|
|
|
|
|
def create_dir_if_not_exists(build_dir: str) -> bool:
|
|
"""Create directory if it doesn't exist"""
|
|
created_build_dir = False
|
|
|
|
if os.path.exists(build_dir):
|
|
if not os.path.isdir(build_dir):
|
|
raise ValueError(f"{build_dir} exists but is not a directory")
|
|
else:
|
|
os.makedirs(build_dir, exist_ok=True)
|
|
created_build_dir = True
|
|
|
|
return created_build_dir
|
|
|
|
|
|
def generate_config(config_name: str) -> None:
|
|
"""Generate lv_conf.h from the defaults file for the given config"""
|
|
print(f"Generating config for {config_name}")
|
|
|
|
build_dir = get_build_dir(config_name)
|
|
create_dir_if_not_exists(build_dir)
|
|
|
|
defaults_file = benchmark_configs[config_name]["defaults_file"]
|
|
defaults_path = os.path.join(lvgl_root_dir, "configs", "ci", "perf", defaults_file)
|
|
template_path = os.path.join(lvgl_root_dir, "lv_conf_template.h")
|
|
|
|
assert os.path.exists(defaults_path)
|
|
assert template_path
|
|
|
|
output_path = os.path.join(build_dir, "lv_conf.h")
|
|
generate_script = os.path.join(lvgl_root_dir, "scripts", "generate_lv_conf.py")
|
|
|
|
cmd = [
|
|
sys.executable,
|
|
generate_script,
|
|
"--template",
|
|
template_path,
|
|
"--config",
|
|
output_path,
|
|
"--defaults",
|
|
defaults_path,
|
|
build_dir,
|
|
]
|
|
|
|
subprocess.check_call(cmd)
|
|
print(f"Generated lv_conf.h at {output_path}")
|
|
|
|
|
|
def generate_benchmark_files(config_name: str) -> None:
|
|
"""Generate the necessary files for running benchmark inside so3"""
|
|
print()
|
|
print()
|
|
label = f"Generating benchmark files for: {config_name}: {benchmark_configs[config_name]['description']}"
|
|
print("=" * len(label))
|
|
print(label)
|
|
print("=" * len(label))
|
|
|
|
build_dir = get_build_dir(config_name)
|
|
create_dir_if_not_exists(build_dir)
|
|
|
|
generate_config(config_name)
|
|
|
|
|
|
def clean(config_name: str) -> None:
|
|
"""Clean build directory and docker resources"""
|
|
print(f"Cleaning resources for {config_name}")
|
|
|
|
build_dir = get_build_dir(config_name)
|
|
container_name = get_container_name(config_name)
|
|
|
|
if os.path.exists(build_dir):
|
|
shutil.rmtree(build_dir)
|
|
print(f"Removed build directory: {build_dir}")
|
|
|
|
subprocess.check_call(
|
|
["docker", "rm", "-f", container_name],
|
|
)
|
|
|
|
for volume in get_docker_volumes(config_name):
|
|
subprocess.check_call(
|
|
["docker", "volume", "remove", "-f", volume],
|
|
)
|
|
|
|
|
|
def run_benchmark(config_name: str, pull: bool) -> None:
|
|
"""Run the benchmark using docker"""
|
|
|
|
def volume(src, dst):
|
|
return ["-v", f"{src}:{dst}"]
|
|
|
|
def so3_usr_lib(path):
|
|
return f"/so3/usr/lib/{path}"
|
|
|
|
so3_usr_build = "/so3/usr/build"
|
|
persistence_dir = "/persistence"
|
|
container_name = get_container_name(config_name)
|
|
build_dir = get_build_dir(config_name)
|
|
|
|
for v in get_docker_volumes(config_name):
|
|
subprocess.check_call(["docker", "volume", "create", v])
|
|
|
|
lvgl_src_path = os.path.join(lvgl_root_dir, "src")
|
|
lvgl_h_path = os.path.join(lvgl_root_dir, "lvgl.h")
|
|
lvgl_demos_path = os.path.join(lvgl_root_dir, "demos")
|
|
lv_conf_path = os.path.join(build_dir, "lv_conf.h")
|
|
if not os.path.exists(lv_conf_path):
|
|
generate_config(config_name)
|
|
assert os.path.exists(lv_conf_path)
|
|
|
|
docker_image_name = benchmark_configs[config_name]["image_name"]
|
|
|
|
volumes = [
|
|
# This is necessary to create a loop device
|
|
volume("/dev", "/dev"),
|
|
# Replace container's lvgl source and lv_conf
|
|
volume(lvgl_h_path, so3_usr_lib("lvgl/lvgl.h")),
|
|
volume(lvgl_src_path, so3_usr_lib("lvgl/src")),
|
|
volume(lvgl_demos_path, so3_usr_lib("lvgl/demos")),
|
|
volume(lv_conf_path, so3_usr_lib("lv_conf.h")),
|
|
# Cache build and disk folders so we don't regenerate everything in consecutive runs
|
|
volume(get_build_cache_volume(config_name), so3_usr_build),
|
|
volume(get_disk_cache_volume(config_name), persistence_dir),
|
|
]
|
|
|
|
interactive = "-it" if sys.stdout.isatty() else "-t"
|
|
command = [
|
|
"docker",
|
|
"run",
|
|
"--rm",
|
|
"--privileged",
|
|
interactive,
|
|
"--name",
|
|
container_name,
|
|
]
|
|
|
|
if pull:
|
|
command.append("--pull=always")
|
|
|
|
for v in volumes:
|
|
command.extend(v)
|
|
|
|
command.append(docker_image_name)
|
|
|
|
print()
|
|
print()
|
|
label = f"Running benchmark: {config_name}: {benchmark_configs[config_name]['description']}"
|
|
print("=" * len(label))
|
|
print(label)
|
|
print("=" * len(label))
|
|
|
|
subprocess.check_call(command)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|