mirror of
https://github.com/esphome/esphome.git
synced 2026-07-11 01:15:33 +00:00
Clean up shared constants and setup script
- Move BASE_CODEGEN_COMPONENTS and USE_TIME_TIMEZONE_FLAG to test_helpers.py - Use shared constants in both cpp_unit_test.py and cpp_benchmark.py - Move json import to top level in cpp_benchmark.py - Refactor setup_codspeed_lib.py into focused helper functions - Combine clone + submodule init, use --shallow-submodules
This commit is contained in:
+135
-77
@@ -2,14 +2,22 @@
|
||||
"""Set up CodSpeed's google_benchmark fork as a PlatformIO library.
|
||||
|
||||
CodSpeed requires their codspeed-cpp fork for CPU simulation instrumentation.
|
||||
This script clones the repo and creates a flat PlatformIO-compatible library
|
||||
by combining google_benchmark sources and codspeed core sources.
|
||||
This script clones the repo and assembles a flat PlatformIO-compatible library
|
||||
by combining google_benchmark sources, codspeed core, and instrument-hooks.
|
||||
|
||||
PlatformIO quirks addressed:
|
||||
- .cc files renamed to .cpp (PlatformIO ignores .cc)
|
||||
- All sources merged into one src/ dir (PlatformIO can't compile from
|
||||
multiple source directories in a single library)
|
||||
- library.json created with required CodSpeed preprocessor defines
|
||||
|
||||
Usage:
|
||||
python script/setup_codspeed_lib.py [--output-dir DIR]
|
||||
|
||||
Prints JSON to stdout with lib_path and build_flags for cpp_benchmark.py.
|
||||
Prints JSON to stdout with lib_path for cpp_benchmark.py.
|
||||
Git output goes to stderr.
|
||||
|
||||
See https://codspeed.io/docs/benchmarks/cpp#custom-build-systems
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -27,95 +35,110 @@ CODSPEED_CPP_SHA = "d6b4111428ae1f1667fec9bff009522378d5d347"
|
||||
|
||||
DEFAULT_OUTPUT_DIR = "/tmp/codspeed-cpp"
|
||||
|
||||
# Well-known paths within the codspeed-cpp repository
|
||||
GOOGLE_BENCHMARK_SUBDIR = "google_benchmark"
|
||||
CORE_SUBDIR = "core"
|
||||
INSTRUMENT_HOOKS_SUBDIR = Path(CORE_SUBDIR) / "instrument-hooks"
|
||||
INSTRUMENT_HOOKS_INCLUDES = INSTRUMENT_HOOKS_SUBDIR / "includes"
|
||||
INSTRUMENT_HOOKS_DIST = INSTRUMENT_HOOKS_SUBDIR / "dist" / "core.c"
|
||||
CORE_CMAKE = Path(CORE_SUBDIR) / "CMakeLists.txt"
|
||||
|
||||
def setup_codspeed_lib(output_dir: Path) -> None:
|
||||
"""Clone codspeed-cpp and create a flat PlatformIO library.
|
||||
|
||||
Args:
|
||||
output_dir: Directory to clone into
|
||||
"""
|
||||
if not (output_dir / ".git").exists():
|
||||
subprocess.run(
|
||||
["git", "clone", CODSPEED_CPP_REPO, str(output_dir)],
|
||||
check=True,
|
||||
stdout=sys.stderr,
|
||||
stderr=sys.stderr,
|
||||
)
|
||||
# Checkout pinned SHA and init submodules in one pass
|
||||
subprocess.run(
|
||||
["git", "-C", str(output_dir), "checkout", CODSPEED_CPP_SHA],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
str(output_dir),
|
||||
"submodule",
|
||||
"update",
|
||||
"--init",
|
||||
"--recursive",
|
||||
],
|
||||
check=True,
|
||||
stdout=sys.stderr,
|
||||
stderr=sys.stderr,
|
||||
)
|
||||
def _git(args: list[str], **kwargs: object) -> None:
|
||||
"""Run a git command, sending output to stderr."""
|
||||
subprocess.run(
|
||||
["git", *args],
|
||||
check=True,
|
||||
stdout=kwargs.pop("stdout", sys.stderr),
|
||||
stderr=kwargs.pop("stderr", sys.stderr),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
benchmark_dir = output_dir / "google_benchmark"
|
||||
core_dir = output_dir / "core"
|
||||
instrument_hooks_dir = core_dir / "instrument-hooks" / "includes"
|
||||
|
||||
# Read version from core/CMakeLists.txt (needed by walltime.cpp)
|
||||
version = "0.0.0"
|
||||
cmake_file = core_dir / "CMakeLists.txt"
|
||||
if cmake_file.exists():
|
||||
for line in cmake_file.read_text().splitlines():
|
||||
if line.startswith("set(CODSPEED_VERSION"):
|
||||
version = line.split()[1].rstrip(")")
|
||||
break
|
||||
def _clone_repo(output_dir: Path) -> None:
|
||||
"""Clone codspeed-cpp at the pinned SHA with submodules."""
|
||||
_git(
|
||||
[
|
||||
"clone",
|
||||
"--recurse-submodules",
|
||||
"--shallow-submodules",
|
||||
CODSPEED_CPP_REPO,
|
||||
str(output_dir),
|
||||
]
|
||||
)
|
||||
_git(
|
||||
[
|
||||
"-C",
|
||||
str(output_dir),
|
||||
"-c",
|
||||
"advice.detachedHead=false",
|
||||
"checkout",
|
||||
CODSPEED_CPP_SHA,
|
||||
],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
# PlatformIO doesn't compile .cc files — rename to .cpp
|
||||
for cc_file in (benchmark_dir / "src").glob("*.cc"):
|
||||
|
||||
def _read_codspeed_version(cmake_path: Path) -> str:
|
||||
"""Extract CODSPEED_VERSION from core/CMakeLists.txt."""
|
||||
if not cmake_path.exists():
|
||||
return "0.0.0"
|
||||
for line in cmake_path.read_text().splitlines():
|
||||
if line.startswith("set(CODSPEED_VERSION"):
|
||||
return line.split()[1].rstrip(")")
|
||||
return "0.0.0"
|
||||
|
||||
|
||||
def _rename_cc_to_cpp(src_dir: Path) -> None:
|
||||
"""Rename .cc files to .cpp so PlatformIO compiles them."""
|
||||
for cc_file in src_dir.glob("*.cc"):
|
||||
cpp_file = cc_file.with_suffix(".cpp")
|
||||
if not cpp_file.exists():
|
||||
cc_file.rename(cpp_file)
|
||||
|
||||
# Copy codspeed core sources and headers into google_benchmark/src/
|
||||
# so PlatformIO compiles everything as one library.
|
||||
# .cpp files get a codspeed_ prefix to avoid name collisions.
|
||||
# .h files keep their original names since they're referenced by includes.
|
||||
for src_file in (core_dir / "src").glob("*"):
|
||||
|
||||
def _copy_if_missing(src: Path, dest: Path) -> None:
|
||||
"""Copy a file only if the destination doesn't already exist."""
|
||||
if not dest.exists():
|
||||
shutil.copy2(src, dest)
|
||||
|
||||
|
||||
def _merge_codspeed_core_into_lib(core_src: Path, lib_src: Path) -> None:
|
||||
"""Copy codspeed core sources into the benchmark library src/.
|
||||
|
||||
.cpp files get a ``codspeed_`` prefix to avoid name collisions with
|
||||
google_benchmark's own sources. .h files keep their original names
|
||||
since they're referenced by ``#include "walltime.h"`` etc.
|
||||
"""
|
||||
for src_file in core_src.iterdir():
|
||||
if src_file.suffix == ".cpp":
|
||||
dest = benchmark_dir / "src" / f"codspeed_{src_file.name}"
|
||||
_copy_if_missing(src_file, lib_src / f"codspeed_{src_file.name}")
|
||||
elif src_file.suffix == ".h":
|
||||
dest = benchmark_dir / "src" / src_file.name
|
||||
else:
|
||||
continue
|
||||
if not dest.exists():
|
||||
shutil.copy2(src_file, dest)
|
||||
_copy_if_missing(src_file, lib_src / src_file.name)
|
||||
|
||||
# Copy instrument-hooks C source (provides instrument_hooks_* symbols)
|
||||
hooks_c = instrument_hooks_dir.parent / "dist" / "core.c"
|
||||
if hooks_c.exists():
|
||||
dest = benchmark_dir / "src" / "instrument_hooks.c"
|
||||
if not dest.exists():
|
||||
shutil.copy2(hooks_c, dest)
|
||||
|
||||
# Resolve the ESPHome project root for CODSPEED_ROOT_DIR
|
||||
project_root = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Create library.json
|
||||
def _write_library_json(
|
||||
benchmark_dir: Path,
|
||||
core_include: Path,
|
||||
hooks_include: Path,
|
||||
version: str,
|
||||
project_root: Path,
|
||||
) -> None:
|
||||
"""Write a PlatformIO library.json with CodSpeed build flags."""
|
||||
library_json = {
|
||||
"name": "benchmark",
|
||||
"version": "0.0.0",
|
||||
"build": {
|
||||
"flags": [
|
||||
f"-I{core_dir / 'include'}",
|
||||
f"-I{instrument_hooks_dir}",
|
||||
f"-I{core_include}",
|
||||
f"-I{hooks_include}",
|
||||
# google benchmark build flags
|
||||
"-DHAVE_STD_REGEX",
|
||||
"-DHAVE_STEADY_CLOCK",
|
||||
"-DBENCHMARK_STATIC_DEFINE",
|
||||
# CodSpeed instrumentation flags
|
||||
# https://codspeed.io/docs/benchmarks/cpp#custom-build-systems
|
||||
"-DCODSPEED_ENABLED",
|
||||
"-DCODSPEED_SIMULATION",
|
||||
f'-DCODSPEED_VERSION=\\"{version}\\"',
|
||||
@@ -125,16 +148,52 @@ def setup_codspeed_lib(output_dir: Path) -> None:
|
||||
"includeDir": "include",
|
||||
},
|
||||
}
|
||||
|
||||
(benchmark_dir / "library.json").write_text(
|
||||
json.dumps(library_json, indent=2) + "\n"
|
||||
)
|
||||
|
||||
|
||||
def setup_codspeed_lib(output_dir: Path) -> None:
|
||||
"""Clone codspeed-cpp and assemble a flat PlatformIO library.
|
||||
|
||||
The resulting library at ``output_dir/google_benchmark/`` contains:
|
||||
- google_benchmark sources (.cc renamed to .cpp)
|
||||
- codspeed core sources (prefixed ``codspeed_``)
|
||||
- instrument-hooks C source (as ``instrument_hooks.c``)
|
||||
- library.json with all required CodSpeed defines
|
||||
|
||||
Args:
|
||||
output_dir: Directory to clone the repository into
|
||||
"""
|
||||
if not (output_dir / ".git").exists():
|
||||
_clone_repo(output_dir)
|
||||
|
||||
benchmark_dir = output_dir / GOOGLE_BENCHMARK_SUBDIR
|
||||
lib_src = benchmark_dir / "src"
|
||||
core_dir = output_dir / CORE_SUBDIR
|
||||
core_include = core_dir / "include"
|
||||
hooks_include = output_dir / INSTRUMENT_HOOKS_INCLUDES
|
||||
hooks_dist_c = output_dir / INSTRUMENT_HOOKS_DIST
|
||||
project_root = Path(__file__).resolve().parent.parent
|
||||
|
||||
# 1. Rename .cc → .cpp (PlatformIO doesn't compile .cc)
|
||||
_rename_cc_to_cpp(lib_src)
|
||||
|
||||
# 2. Merge codspeed core sources into the library
|
||||
_merge_codspeed_core_into_lib(core_dir / "src", lib_src)
|
||||
|
||||
# 3. Copy instrument-hooks C source (provides instrument_hooks_* symbols)
|
||||
if hooks_dist_c.exists():
|
||||
_copy_if_missing(hooks_dist_c, lib_src / "instrument_hooks.c")
|
||||
|
||||
# 4. Write library.json
|
||||
version = _read_codspeed_version(output_dir / CORE_CMAKE)
|
||||
_write_library_json(
|
||||
benchmark_dir, core_include, hooks_include, version, project_root
|
||||
)
|
||||
|
||||
# Output JSON config for cpp_benchmark.py
|
||||
result = {
|
||||
"lib_path": str(benchmark_dir),
|
||||
}
|
||||
print(json.dumps(result))
|
||||
print(json.dumps({"lib_path": str(benchmark_dir)}))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -146,7 +205,6 @@ def main() -> None:
|
||||
help=f"Directory to clone codspeed-cpp into (default: {DEFAULT_OUTPUT_DIR})",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_codspeed_lib(args.output_dir)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user