blob: 86f43ee2f4fd69f59c58ecae213e075b9ef1446f [file]
# Copyright 2025 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
import argparse
import pathlib
import io
import sys
from typing import List
def get_bazel_files(root_dir: pathlib.Path) -> List[pathlib.Path]:
"""Finds files matching "BUILD.bazel" or "*.bzl" under the given directory.
Args:
root_dir: The root directory to search in.
Returns:
A list of pathlib.Path objects representing the matching files.
"""
return [
path
for path in root_dir.rglob("*")
if path.match("BUILD.bazel") or path.match("*.bzl")
]
def get_py_files(root_dir: pathlib.Path) -> List[pathlib.Path]:
"""Finds files matching "*.py" under the given directory.
Args:
root_dir: The root directory to search in.
Returns:
A list of pathlib.Path objects representing the matching files.
"""
return [
path
for path in root_dir.rglob("*")
if path.match("*.py")
]
def get_cc_files(root_dir: pathlib.Path) -> List[pathlib.Path]:
"""Finds C/C++ and linker files under the given directory.
Args:
root_dir: The root directory to search in.
Returns:
A list of pathlib.Path objects representing the matching files.
"""
patterns = (
"Kconfig*",
"*.c",
"*.cc",
"*.dts",
"*.dtsi",
"*.h",
"*.ld",
"*.S",
"*.yml",
)
return [
path
for path in root_dir.rglob("*")
if any(path.match(p) for p in patterns)
]
def print_file_info(
file_path: pathlib.Path,
root_dir: pathlib.Path,
writer: io.TextIOWrapper,
):
"""Prints the relative path, line count, and prefixed content of a file.
Args:
file_path: The path to the file.
root_dir: The root directory to calculate the relative path from.
writer: Where to write the output
"""
relative_path = file_path.relative_to(root_dir)
with open(file_path, "r", encoding="utf-8") as file:
content = file.readlines()
line_count = len(content)
writer.write("\n")
writer.write("--- /dev/null\n")
writer.write(f"+++ b/{relative_path}\n")
writer.write(f"@@ -0,0 +1,{line_count} @@\n")
for line in content:
writer.write(f"+{line.rstrip()}\n")
def print_all(root_dir: pathlib.Path, writer: io.TextIOWrapper):
"""Iterate through all BUILD.bazel and *.bzl files and write them to writer
Args:
root_dir: The root directory to calculate the relative path from.
writer: Where to write the output
"""
file_paths = get_bazel_files(root_dir=root_dir)
file_paths += get_py_files(root_dir=root_dir)
file_paths += get_cc_files(root_dir=root_dir)
for file_path in file_paths:
print_file_info(file_path=file_path, root_dir=root_dir, writer=writer)
def append_custom_patches(
root_dir: pathlib.Path,
custom_patches_dir_name: str,
writer: io.TextIOWrapper,
):
custom_patches_dir = root_dir / custom_patches_dir_name
if custom_patches_dir.exists():
for path in custom_patches_dir.glob("*.patch"):
with open(path, "r", encoding="utf-8") as f:
writer.write(f.read())
def main() -> None:
"""Finds all BUILD.bazel files and *.bzl files and generates a diff"""
parser = argparse.ArgumentParser()
parser.add_argument(
"--root-dir",
type=pathlib.Path,
nargs="+",
)
parser.add_argument(
"-o",
dest="output",
type=pathlib.Path,
)
parser.add_argument(
"--custom-patches-dir",
type=str,
default="custom_patches",
help="Directory name for custom patches, relative to root-dir",
)
args = parser.parse_args()
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
with open(args.output, mode="w", encoding="utf-8") as o:
for root_dir in args.root_dir:
print_all(root_dir=root_dir, writer=o)
append_custom_patches(
root_dir=root_dir,
custom_patches_dir_name=args.custom_patches_dir,
writer=o,
)
else:
writer = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
for root_dir in args.root_dir:
print_all(root_dir=root_dir, writer=writer)
append_custom_patches(
root_dir=root_dir,
custom_patches_dir_name=args.custom_patches_dir,
writer=writer,
)
writer.flush()
if __name__ == "__main__":
main()