blob: a957f866d3df709aad06b99cf6c9d1c80c1e069c [file]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2025 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates a weights file from a ninja log.
This script reads a ninja .ninja_log file and extracts build durations for each
target. It filters out durations below a specified minimum and writes the
results to a weights file, sorted by duration in descending order.
"""
import argparse
import os
import sys
from pathlib import Path
def convert_log_to_weights(
ninjalog_path: Path,
weights_path: Path,
minimum_duration_ms: int,
) -> None:
weights: dict[str, int] = {}
last_stop = 0
with open(ninjalog_path) as logfile:
for linenum, line in enumerate(logfile.readlines()):
if line.startswith("#"):
continue
tokens = line.split("\t")
start = int(tokens[0])
stop = int(tokens[1])
path = tokens[3]
# Make sure that the file is in order, and not from an incremental
# build.
if stop < last_stop:
raise ValueError(
f"File appears to contain entries from multiple builds, this is an error. Use a log file from a single, clean, build. {last_stop} {stop} {path}"
)
last_stop = stop
if path in weights:
raise ValueError(
f"Found duplicate entries for path: {path} at line {linenum+1}"
)
duration = stop - start
if duration > minimum_duration_ms:
weights[path] = stop - start
print(f"read {len(weights)} weights from ninja log")
os.makedirs(weights_path.parent, exist_ok=True)
with open(weights_path, "w") as weights_file:
for path, weight in sorted(
weights.items(), key=lambda x: x[1], reverse=True
):
print(f"{path},{weight}", file=weights_file)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--ninja-log",
type=Path,
required=True,
help="Path to a ninja log file, it must only contain a SINGLE build's output",
)
parser.add_argument(
"--weights-file",
type=Path,
required=True,
help="Path to a file to write ninja weights to.",
)
parser.add_argument(
"--minimum-duration-ms",
type=int,
default=60000,
help="Minimum duration in ms to use as a weight.",
)
args = parser.parse_args()
print(f"reading ninja log: {args.ninja_log}")
convert_log_to_weights(
args.ninja_log, args.weights_file, args.minimum_duration_ms
)
return 0
if __name__ == "__main__":
sys.exit(main())