233 lines
7.3 KiB
Python
233 lines
7.3 KiB
Python
import argparse
|
|
import csv
|
|
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import dateutil.parser # type: ignore
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
|
|
|
|
def run_command(command):
|
|
process = subprocess.Popen(
|
|
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
|
|
)
|
|
output, error = process.communicate()
|
|
return output.decode("utf-8"), error.decode("utf-8")
|
|
|
|
|
|
def step_back_commits(steps):
|
|
step_back_command = f"git reset --hard HEAD~{steps}"
|
|
output, error = run_command(step_back_command)
|
|
if error:
|
|
print(f"Error stepping back {steps} commit(s): {error}")
|
|
return False
|
|
return True
|
|
|
|
|
|
def check_firmware_size(board: str) -> int:
|
|
root_build_dir = Path(".build") / board
|
|
build_info_json = root_build_dir / "build_info.json"
|
|
build_info = json.loads(build_info_json.read_text())
|
|
board_info = build_info.get(board)
|
|
assert board_info, f"Board {board} not found in {build_info_json}"
|
|
prog_path = Path(board_info["prog_path"])
|
|
base_path = prog_path.parent
|
|
suffixes = [".bin", ".hex", ".uf2"]
|
|
firmware: Path
|
|
for suffix in suffixes:
|
|
firmware = base_path / f"firmware{suffix}"
|
|
if firmware.exists():
|
|
break
|
|
else:
|
|
msg = (
|
|
", ".join([f"firmware{suffix}" for suffix in suffixes])
|
|
+ f" not found in {base_path}"
|
|
)
|
|
raise FileNotFoundError(msg)
|
|
size_command = f"du -b {firmware}"
|
|
output, error = run_command(size_command)
|
|
if error:
|
|
print(f"Error checking firmware size: {error}")
|
|
return -1
|
|
size_in_bytes = output.strip().split()[0]
|
|
return int(size_in_bytes)
|
|
|
|
|
|
def get_commit_hash():
|
|
hash_command = "git rev-parse HEAD"
|
|
output, error = run_command(hash_command)
|
|
if error:
|
|
print(f"Error getting commit hash: {error}")
|
|
return None
|
|
return output.strip()
|
|
|
|
|
|
def get_commit_date(commit_hash):
|
|
date_command = f"git show -s --format=%ci {commit_hash}"
|
|
output, error = run_command(date_command)
|
|
if error:
|
|
print(f"Error getting commit date: {error}")
|
|
return None
|
|
return dateutil.parser.parse(output.strip()).isoformat()
|
|
|
|
|
|
def main(
|
|
board: str,
|
|
num_commits: int,
|
|
skip_step: int,
|
|
start_commit: str | None = None,
|
|
end_commit: str | None = None,
|
|
):
|
|
# change to the script dir
|
|
os.chdir(str(HERE))
|
|
# Create tmp directory if it doesn't exist
|
|
|
|
if os.path.exists("tmp"):
|
|
shutil.rmtree("tmp")
|
|
os.makedirs("tmp", exist_ok=True)
|
|
|
|
# Change to the tmp directory
|
|
os.chdir("tmp")
|
|
|
|
# 1. Git clone FastLED repository
|
|
print("Cloning FastLED repository...")
|
|
clone_command = "git clone https://github.com/FastLED/FastLED.git"
|
|
output, error = run_command(clone_command)
|
|
# if error:
|
|
# print(f"Error cloning repository: {error}")
|
|
# os.chdir("..")
|
|
# return
|
|
|
|
# Change to the FastLED directory
|
|
os.chdir("FastLED")
|
|
|
|
# Checkout the latest commit
|
|
run_command("git checkout master")
|
|
|
|
# If end_commit is specified, checkout that commit
|
|
# if end_commit:
|
|
# print(f"Checking out end commit: {end_commit}")
|
|
# checkout_command = f"git checkout {end_commit}"
|
|
# output, error = run_command(checkout_command)
|
|
# #if error:
|
|
# # print(f"Error checking out end commit: {error}")
|
|
# # return
|
|
|
|
# Prepare CSV file
|
|
csv_filename = "../../firmware_sizes.csv"
|
|
with open(csv_filename, "w", newline="") as csvfile:
|
|
csvwriter = csv.writer(csvfile)
|
|
csvwriter.writerow(["datetime", "commit_hash", "binary_size"])
|
|
|
|
commits_checked = 0
|
|
first_iteration = True
|
|
while True:
|
|
current_commit = get_commit_hash()
|
|
|
|
if first_iteration and start_commit:
|
|
first_iteration = False
|
|
while True:
|
|
if current_commit == start_commit:
|
|
break
|
|
if not step_back_commits(1):
|
|
break
|
|
current_commit = get_commit_hash()
|
|
|
|
if num_commits and commits_checked >= num_commits:
|
|
print(f"Checked {num_commits} commits")
|
|
break
|
|
|
|
if end_commit and current_commit == end_commit:
|
|
print(f"Checked until end commit: {end_commit}")
|
|
break
|
|
|
|
# 2. Run ci-compile.py for current commit
|
|
print(f"\nChecking commit {commits_checked + 1}")
|
|
|
|
# remove .build/esp32dev/pio/build/esp32dev/ directory
|
|
board_files = Path(".build") / board / ".pio" / "build" / board
|
|
if board_files.exists():
|
|
shutil.rmtree(str(board_files), ignore_errors=True)
|
|
compile_command = f"python3 ci/ci-compile.py {board} --examples Blink"
|
|
output, error = run_command(compile_command)
|
|
if error:
|
|
print(f"Error running ci-compile.py: {error}")
|
|
if not step_back_commits(skip_step):
|
|
break
|
|
continue
|
|
|
|
# 3. Check firmware size and get commit hash
|
|
print("Checking firmware size...")
|
|
try:
|
|
size = check_firmware_size(board)
|
|
except FileNotFoundError as e:
|
|
print(f"Error checking firmware size: {e}")
|
|
if not step_back_commits(skip_step):
|
|
break
|
|
continue
|
|
except AssertionError as e:
|
|
print(f"Error: {e}")
|
|
if not step_back_commits(skip_step):
|
|
break
|
|
continue
|
|
commit_hash = get_commit_hash()
|
|
if size and commit_hash:
|
|
commit_date = get_commit_date(commit_hash)
|
|
print(f"Firmware size: {size} bytes")
|
|
|
|
# Write to CSV incrementally
|
|
with open(csv_filename, "a", newline="") as csvfile:
|
|
csvwriter = csv.writer(csvfile)
|
|
csvwriter.writerow([commit_date, commit_hash, size])
|
|
|
|
print(f"Result appended to {csv_filename}")
|
|
|
|
commits_checked += 1
|
|
|
|
# 4. Step back one commit
|
|
print("Stepping back 1 commit...")
|
|
if not step_back_commits(1):
|
|
break
|
|
|
|
# Don't remove the tmp directory
|
|
print("\nTemporary directory 'tmp' has been left intact for inspection.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(
|
|
description="Check FastLED firmware size for multiple commits."
|
|
)
|
|
parser.add_argument(
|
|
"--num-commits", type=int, default=1, help="Number of commits to check"
|
|
)
|
|
parser.add_argument(
|
|
"--skip-step",
|
|
type=int,
|
|
default=1,
|
|
help="Number of commits to skip between checks",
|
|
)
|
|
parser.add_argument("--start-commit", type=str, help="Starting commit hash")
|
|
parser.add_argument("--end-commit", type=str, help="Ending commit hash")
|
|
parser.add_argument(
|
|
"--board", type=str, required=True, help="Board to check firmware size for"
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if args.start_commit or args.end_commit:
|
|
if not (args.start_commit and not args.end_commit):
|
|
print("Both start commit and end commit must be specified.")
|
|
exit(1)
|
|
# if start_commit is specified, end_commit must be specified
|
|
|
|
num_commits = args.num_commits
|
|
if args.start_commit and args.end_commit:
|
|
if args.start_commit == args.end_commit:
|
|
print("Start commit and end commit are the same.")
|
|
exit(1)
|
|
num_commits = 999999
|
|
|
|
main(args.board, num_commits, args.skip_step, args.start_commit, args.end_commit)
|