mirror of
https://github.com/esphome/esphome.git
synced 2026-09-15 09:08:41 +00:00
Fix CodSpeed instrumentation with PlatformIO
- Use CodSpeed's codspeed-cpp fork with proper instrumentation for simulation mode benchmark detection - setup_codspeed_lib.py creates a flat PlatformIO-compatible library by combining google_benchmark sources, codspeed core, and instrument-hooks into a single library directory - Renames .cc to .cpp (PlatformIO doesn't compile .cc by default) - Adds all required defines: CODSPEED_ENABLED, CODSPEED_SIMULATION, CODSPEED_VERSION, CODSPEED_ROOT_DIR, CODSPEED_MODE_DISPLAY - Output JSON config consumed by cpp_benchmark.py via env var
This commit is contained in:
@@ -331,9 +331,8 @@ jobs:
|
||||
id: build
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
BENCHMARK_LIB=$(python script/setup_codspeed_lib.py)
|
||||
BENCHMARK_LIB="$BENCHMARK_LIB" \
|
||||
script/cpp_benchmark.py --all --build-only 2>&1 | tee /tmp/bench-build.log
|
||||
export BENCHMARK_LIB_CONFIG=$(python script/setup_codspeed_lib.py)
|
||||
script/cpp_benchmark.py --all --build-only 2>&1 | tee /tmp/bench-build.log
|
||||
BINARY=$(tail -1 /tmp/bench-build.log)
|
||||
echo "binary=$BINARY" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
+16
-2
@@ -26,12 +26,26 @@ PLATFORMIO_OPTIONS = {
|
||||
"-DUSE_TIME_TIMEZONE", # enable timezone code paths
|
||||
"-g", # debug symbols for profiling
|
||||
],
|
||||
# Use deep+ LDF mode to ensure PlatformIO detects the benchmark
|
||||
# library dependency from nested includes.
|
||||
"lib_ldf_mode": "deep+",
|
||||
}
|
||||
|
||||
|
||||
def run_benchmarks(selected_components: list[str], build_only: bool = False) -> int:
|
||||
# Allow CI to override the benchmark library (e.g. with CodSpeed's fork)
|
||||
benchmark_lib = os.environ.get("BENCHMARK_LIB", PLATFORMIO_GOOGLE_BENCHMARK_LIB)
|
||||
# Allow CI to override the benchmark library (e.g. with CodSpeed's fork).
|
||||
# BENCHMARK_LIB_CONFIG is a JSON string from setup_codspeed_lib.py
|
||||
# containing {"lib_path": "/path/to/google_benchmark"}.
|
||||
lib_config_json = os.environ.get("BENCHMARK_LIB_CONFIG")
|
||||
|
||||
if lib_config_json:
|
||||
import json
|
||||
|
||||
lib_config = json.loads(lib_config_json)
|
||||
benchmark_lib = f"benchmark=symlink://{lib_config['lib_path']}"
|
||||
else:
|
||||
benchmark_lib = PLATFORMIO_GOOGLE_BENCHMARK_LIB
|
||||
|
||||
return build_and_run(
|
||||
selected_components=selected_components,
|
||||
tests_dir=BENCHMARKS_DIR,
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
"""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 PlatformIO-compatible library.json
|
||||
that combines the google_benchmark and codspeed core sources.
|
||||
This script clones the repo and creates a flat PlatformIO-compatible library
|
||||
by combining google_benchmark sources and codspeed core sources.
|
||||
|
||||
Usage:
|
||||
python script/setup_codspeed_lib.py [--output-dir DIR]
|
||||
|
||||
Prints the PlatformIO library path (symlink:// URL) to stdout.
|
||||
Prints JSON to stdout with lib_path and build_flags for cpp_benchmark.py.
|
||||
Git output goes to stderr.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -16,7 +17,9 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Pin to a specific commit for reproducibility
|
||||
CODSPEED_CPP_REPO = "https://github.com/CodSpeedHQ/codspeed-cpp.git"
|
||||
@@ -25,44 +28,99 @@ CODSPEED_CPP_SHA = "d6b4111428ae1f1667fec9bff009522378d5d347"
|
||||
DEFAULT_OUTPUT_DIR = "/tmp/codspeed-cpp"
|
||||
|
||||
|
||||
def setup_codspeed_lib(output_dir: Path) -> str:
|
||||
"""Clone codspeed-cpp and create PlatformIO library layout.
|
||||
def setup_codspeed_lib(output_dir: Path) -> None:
|
||||
"""Clone codspeed-cpp and create a flat PlatformIO library.
|
||||
|
||||
Args:
|
||||
output_dir: Directory to clone into
|
||||
|
||||
Returns:
|
||||
PlatformIO library path (symlink:// URL)
|
||||
"""
|
||||
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,
|
||||
)
|
||||
subprocess.run(
|
||||
["git", "-C", str(output_dir), "checkout", CODSPEED_CPP_SHA],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
benchmark_dir = output_dir / "google_benchmark"
|
||||
core_dir = output_dir / "core"
|
||||
instrument_hooks_dir = core_dir / "instrument-hooks" / "includes"
|
||||
|
||||
# Create library.json combining google_benchmark + codspeed core
|
||||
# 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
|
||||
|
||||
# PlatformIO doesn't compile .cc files — rename to .cpp
|
||||
for cc_file in (benchmark_dir / "src").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("*"):
|
||||
if src_file.suffix == ".cpp":
|
||||
dest = benchmark_dir / "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 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
|
||||
library_json = {
|
||||
"name": "benchmark",
|
||||
"version": "0.0.0",
|
||||
"build": {
|
||||
"flags": [
|
||||
f"-I{core_dir / 'include'}",
|
||||
f"-I{core_dir / 'instrument-hooks'}",
|
||||
f"-I{instrument_hooks_dir}",
|
||||
"-DHAVE_STD_REGEX",
|
||||
"-DHAVE_STEADY_CLOCK",
|
||||
"-DBENCHMARK_STATIC_DEFINE",
|
||||
],
|
||||
"srcFilter": [
|
||||
"+<src/*.cc>",
|
||||
f"+<{core_dir / 'src' / '*.cpp'}>",
|
||||
"-DCODSPEED_ENABLED",
|
||||
"-DCODSPEED_SIMULATION",
|
||||
f'-DCODSPEED_VERSION=\\"{version}\\"',
|
||||
f'-DCODSPEED_ROOT_DIR=\\"{project_root}\\"',
|
||||
'-DCODSPEED_MODE_DISPLAY=\\"simulation\\"',
|
||||
],
|
||||
"includeDir": "include",
|
||||
},
|
||||
@@ -72,7 +130,11 @@ def setup_codspeed_lib(output_dir: Path) -> str:
|
||||
json.dumps(library_json, indent=2) + "\n"
|
||||
)
|
||||
|
||||
return f"symlink://{benchmark_dir}"
|
||||
# Output JSON config for cpp_benchmark.py
|
||||
result = {
|
||||
"lib_path": str(benchmark_dir),
|
||||
}
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -85,8 +147,7 @@ def main() -> None:
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
lib_path = setup_codspeed_lib(args.output_dir)
|
||||
print(lib_path)
|
||||
setup_codspeed_lib(args.output_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user