mirror of
https://github.com/signalapp/libsignal.git
synced 2024-09-19 19:42:19 +02:00
823cfcf7e8
...specifically in the case where the most recent commit didn't run the Java checks, and therefore we don't have a previous run of check_code_size to compare to. (We could go back until we find one, but that seems like overkill.)
135 lines
5.3 KiB
Python
Executable File
135 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
#
|
|
# Copyright (C) 2021 Signal Messenger, LLC.
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
#
|
|
|
|
import glob
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
from typing import Any, Callable, Iterable, List, Mapping, Optional, TypeVar
|
|
|
|
T = TypeVar('T')
|
|
|
|
|
|
def warn(message: str) -> None:
|
|
if 'GITHUB_ACTIONS' in os.environ:
|
|
print("::warning ::" + message)
|
|
else:
|
|
print("warning: " + message, file=sys.stderr)
|
|
|
|
|
|
def measure_stripped_library_size(lib_path: str) -> int:
|
|
ndk_home = os.environ.get('ANDROID_NDK_HOME')
|
|
if not ndk_home:
|
|
raise Exception("must set ANDROID_NDK_HOME to an Android NDK to run this script")
|
|
|
|
strip_glob = os.path.join(ndk_home, 'toolchains', 'llvm', 'prebuilt', '*', 'bin', 'llvm-strip')
|
|
strip = next(glob.iglob(strip_glob), None)
|
|
if not strip:
|
|
raise Exception("NDK does not contain llvm-strip (tried {})".format(strip_glob))
|
|
|
|
return len(subprocess.check_output([strip, '-o', '-', lib_path]))
|
|
|
|
|
|
def print_size_diff(lib_size: int, old_entry: Mapping[str, Any], *, warn_on_jump: bool = True) -> None:
|
|
delta = lib_size - old_entry['size']
|
|
delta_fraction = (float(delta) / old_entry['size'])
|
|
message = f"current build is {delta} bytes ({int(delta_fraction * 100)}%) larger than {old_entry['version']}"
|
|
if warn_on_jump and delta > 100_000:
|
|
warn(message)
|
|
else:
|
|
print(message)
|
|
|
|
|
|
def current_origin_main_entry() -> Optional[Mapping[str, Any]]:
|
|
try:
|
|
if os.environ.get('GITHUB_EVENT_NAME') == 'push':
|
|
base_ref = os.environ.get('GITHUB_REF_NAME', 'HEAD^')
|
|
most_recent_commit = subprocess.run(["git", "rev-parse", "HEAD^"], capture_output=True, check=True).stdout.decode().strip()
|
|
else:
|
|
base_ref = os.environ.get('GITHUB_BASE_REF', 'main')
|
|
remote_name = os.environ.get('CHECK_CODE_SIZE_REMOTE', 'origin')
|
|
most_recent_commit = subprocess.run(["git", "merge-base", "HEAD", f"{remote_name}/{base_ref}"], capture_output=True, check=True).stdout.decode().strip()
|
|
|
|
repo_path = os.environ.get('GITHUB_REPOSITORY')
|
|
if repo_path is None:
|
|
repo_path = subprocess.run(["gh", "repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"], capture_output=True, check=True).stdout.decode().strip()
|
|
|
|
runs_info = subprocess.run(["gh", "api", "--method=GET", f"repos/{repo_path}/actions/runs", "-f", f"head_sha={most_recent_commit}"], capture_output=True, check=True).stdout
|
|
runs_json = json.loads(runs_info)
|
|
|
|
run_id = [run['id'] for run in runs_json['workflow_runs'] if run['name'] == 'Build and Test'][0]
|
|
|
|
run_jobs = subprocess.run(["gh", "run", "view", "-R", repo_path, f"{run_id}", "--json", "jobs"], capture_output=True, check=True).stdout
|
|
jobs_json = json.loads(run_jobs)
|
|
|
|
job_id = [job['databaseId'] for job in jobs_json['jobs'] if job['name'] == "Java"][0]
|
|
|
|
job_logs = subprocess.run(["gh", "run", "view", "-R", repo_path, "--job", f"{job_id}", "--log"], capture_output=True, check=True).stdout.decode()
|
|
|
|
for line in job_logs.splitlines():
|
|
if "check_code_size.py" in line and "current: *" in line:
|
|
(_, after) = line.split("(", maxsplit=1)
|
|
(bytes_count, _) = after.split(" bytes)", maxsplit=1)
|
|
return {'size': int(bytes_count), 'version': f"{most_recent_commit[:6]} ({base_ref})"}
|
|
|
|
print(f"skipping checking current {base_ref} (most recent run did not include check_code_size.py)", file=sys.stderr)
|
|
|
|
except Exception as e:
|
|
print(f"skipping checking current {base_ref}: {e}", file=sys.stderr)
|
|
if isinstance(e, subprocess.CalledProcessError):
|
|
print("stdout:", e.stdout.decode(), file=sys.stderr)
|
|
print("stderr:", e.stderr.decode(), file=sys.stderr)
|
|
|
|
return None
|
|
|
|
|
|
our_abs_dir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
lib_size = measure_stripped_library_size(os.path.join(
|
|
our_abs_dir, 'android', 'src', 'main', 'jniLibs', 'arm64-v8a', 'libsignal_jni.so'))
|
|
|
|
with open(os.path.join(our_abs_dir, 'code_size.json')) as old_sizes_file:
|
|
old_sizes = json.load(old_sizes_file)
|
|
|
|
most_recent_tag_entry = old_sizes[-1]
|
|
origin_main_entry = current_origin_main_entry()
|
|
if origin_main_entry is not None:
|
|
print_size_diff(lib_size, most_recent_tag_entry, warn_on_jump=False)
|
|
print_size_diff(lib_size, origin_main_entry)
|
|
else:
|
|
print_size_diff(lib_size, most_recent_tag_entry)
|
|
|
|
|
|
# Typing this properly requires a bunch of helpers in Python 3.9,
|
|
# and we don't have a strict type at the use site anyway.
|
|
def max_map(items: Iterable[T], transform: Callable[[T], Any]) -> Any:
|
|
return transform(max(items, key=transform))
|
|
|
|
|
|
def print_plot(sizes: List[Mapping[str, Any]]) -> None:
|
|
highest_size = max_map(recent_sizes, lambda x: x['size'])
|
|
version_width = max_map(recent_sizes, lambda x: len(x['version']))
|
|
|
|
scale = 1.0 * 1024 * 1024
|
|
while scale < highest_size:
|
|
scale *= 2
|
|
scale /= 20
|
|
plot_width = int(highest_size / scale) + 1
|
|
|
|
for entry in sizes:
|
|
bucket = int(entry['size'] / scale) + 1
|
|
print('{:>{}}: {:<{}} ({} bytes)'.format(entry['version'], version_width, '*' * bucket, plot_width, entry['size']))
|
|
|
|
|
|
recent_sizes = old_sizes[-10:]
|
|
if origin_main_entry is not None:
|
|
recent_sizes.append(origin_main_entry)
|
|
recent_sizes.append({'version': 'current', 'size': lib_size})
|
|
print_plot(recent_sizes)
|