258 lines
7.2 KiB
Python
Executable File
258 lines
7.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
#
|
|
# Copyright 2023, The Android Open Source Project
|
|
#
|
|
# 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
|
|
#
|
|
# http://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.
|
|
#
|
|
"""Script to prepare an update to a new version of ktfmt."""
|
|
|
|
import subprocess
|
|
import os
|
|
import sys
|
|
import re
|
|
import shutil
|
|
import argparse
|
|
import textwrap
|
|
|
|
tmp_dir = "/tmp/ktfmt"
|
|
zip_path = os.path.join(tmp_dir, "common.zip")
|
|
jar_path = os.path.join(tmp_dir, "framework/ktfmt.jar")
|
|
copy_path = os.path.join(tmp_dir, "copy.jar")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Prepare a repository for the upgrade of ktfmt to a new version."
|
|
)
|
|
parser.add_argument(
|
|
"--build_id",
|
|
required=True,
|
|
help="The build ID of aosp-build-tools-release with the new version of ktfmt"
|
|
)
|
|
parser.add_argument(
|
|
"--bug_id",
|
|
required=True,
|
|
help="The bug ID associated to each CL generated by this tool")
|
|
parser.add_argument(
|
|
"--repo",
|
|
required=True,
|
|
help="The relative path of the repository to upgrade, e.g. 'frameworks/base/'"
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
build_id = args.build_id
|
|
bug_id = args.bug_id
|
|
repo_relative_path = args.repo
|
|
|
|
build_top = os.environ["ANDROID_BUILD_TOP"]
|
|
repo_absolute_path = os.path.join(build_top, repo_relative_path)
|
|
|
|
print("Preparing upgrade of ktfmt from build", build_id)
|
|
os.chdir(repo_absolute_path)
|
|
check_workspace_clean()
|
|
check_branches()
|
|
|
|
print("Downloading ktfmt.jar from aosp-build-tools-release")
|
|
download_jar(build_id)
|
|
|
|
print(f"Creating local branch ktfmt_update1")
|
|
run_cmd(["repo", "start", "ktfmt_update1"])
|
|
|
|
includes_file = find_includes_file(repo_relative_path)
|
|
if includes_file:
|
|
update_includes_file(build_top, includes_file, bug_id)
|
|
else:
|
|
print("No includes file found, skipping first CL")
|
|
|
|
print(f"Creating local branch ktfmt_update2")
|
|
run_cmd(["repo", "start", "--head", "ktfmt_update2"])
|
|
format_files(build_top, includes_file, repo_absolute_path, bug_id)
|
|
|
|
print("Done. You can now submit the generated CL(s), if any.")
|
|
|
|
|
|
def run_cmd(cmd):
|
|
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if result.returncode != 0:
|
|
print("Error running command: {}".format(" ".join(cmd)))
|
|
print("Output: {}".format(result.stderr.decode()))
|
|
sys.exit(1)
|
|
return result.stdout.decode("utf-8")
|
|
|
|
|
|
def is_workspace_clean():
|
|
return run_cmd(["git", "status", "--porcelain"]) == ""
|
|
|
|
|
|
def check_workspace_clean():
|
|
if not is_workspace_clean():
|
|
print(
|
|
"The current repository contains uncommitted changes, please run this script in a clean workspace"
|
|
)
|
|
sys.exit(1)
|
|
|
|
|
|
def check_branches():
|
|
result = run_cmd(["git", "branch"])
|
|
if "ktfmt_update1" in result or "ktfmt_update2" in result:
|
|
print(
|
|
"Branches ktfmt_update1 or ktfmt_update2 already exist, you should delete them before running this script"
|
|
)
|
|
sys.exit(1)
|
|
|
|
|
|
def download_jar(build_id):
|
|
cmd = [
|
|
"/google/data/ro/projects/android/fetch_artifact", "--branch",
|
|
"aosp-build-tools-release", "--bid", build_id, "--target", "linux",
|
|
"build-common-prebuilts.zip", zip_path
|
|
]
|
|
run_cmd(cmd)
|
|
cmd = ["unzip", "-q", "-o", "-d", tmp_dir, zip_path]
|
|
run_cmd(cmd)
|
|
|
|
if not os.path.isfile(jar_path):
|
|
print("Error: {} is not readable".format(jar_path))
|
|
sys.exit(1)
|
|
|
|
|
|
def find_includes_file(repo_relative_path):
|
|
with open("PREUPLOAD.cfg") as f:
|
|
includes_line = [line for line in f if "ktfmt.py" in line][0].split(" ")
|
|
if "-i" not in includes_line:
|
|
return None
|
|
|
|
index = includes_line.index("-i") + 1
|
|
includes_file = includes_line[index][len("${REPO_ROOT}/") +
|
|
len(repo_relative_path):]
|
|
if not os.path.isfile(includes_file):
|
|
print("Error: {} does not exist or is not a file".format(includes_file))
|
|
sys.exit(1)
|
|
return includes_file
|
|
|
|
|
|
def get_included_folders(includes_file):
|
|
with open(includes_file) as f:
|
|
return [line[1:] for line in f.read().splitlines() if line.startswith("+")]
|
|
|
|
|
|
def update_includes_file(build_top, includes_file, bug_id):
|
|
included_folders = get_included_folders(includes_file)
|
|
cmd = [
|
|
f"{build_top}/external/ktfmt/generate_includes_file.py",
|
|
f"--output={includes_file}"
|
|
] + included_folders
|
|
print(f"Updating {includes_file} with the command: {cmd}")
|
|
run_cmd(cmd)
|
|
|
|
if is_workspace_clean():
|
|
print(f"No change were made to {includes_file}, skipping first CL")
|
|
else:
|
|
print(f"Creating first CL with update of {includes_file}")
|
|
create_first_cl(bug_id)
|
|
|
|
|
|
def create_first_cl(bug_id):
|
|
sha1sum = get_sha1sum(jar_path)
|
|
change_id = f"I{sha1sum}"
|
|
command = " ".join(sys.argv)
|
|
cl_message = textwrap.dedent(f"""
|
|
Regenerate include file for ktfmt upgrade
|
|
|
|
This CL was generated automatically from the following command:
|
|
|
|
$ {command}
|
|
|
|
This CL regenerates the inclusion file with the current version of ktfmt
|
|
so that it is up-to-date with files currently formatted or ignored by
|
|
ktfmt.
|
|
|
|
Bug: {bug_id}
|
|
Test: Presubmits
|
|
Change-Id: {change_id}
|
|
Merged-In: {change_id}
|
|
""")
|
|
|
|
run_cmd(["git", "add", "--all"])
|
|
run_cmd(["git", "commit", "-m", cl_message])
|
|
|
|
|
|
def get_sha1sum(file):
|
|
output = run_cmd(["sha1sum", file])
|
|
regex = re.compile(r"[a-f0-9]{40}")
|
|
match = regex.search(output)
|
|
if not match:
|
|
print(f"sha1sum not found in output: {output}")
|
|
sys.exit(1)
|
|
return match.group()
|
|
|
|
|
|
def format_files(build_top, includes_file, repo_absolute_path, bug_id):
|
|
if (includes_file):
|
|
included_folders = get_included_folders(includes_file)
|
|
cmd = [
|
|
f"{build_top}/external/ktfmt/ktfmt.py", "-i", includes_file, "--jar",
|
|
jar_path
|
|
] + included_folders
|
|
else:
|
|
cmd = [
|
|
f"{build_top}/external/ktfmt/ktfmt.py", "--jar", jar_path,
|
|
repo_absolute_path
|
|
]
|
|
|
|
print(
|
|
f"Formatting the files that are already formatted with the command: {cmd}"
|
|
)
|
|
run_cmd(cmd)
|
|
|
|
if is_workspace_clean():
|
|
print("All files were already properly formatted, skipping second CL")
|
|
else:
|
|
print("Creating second CL that formats all files")
|
|
create_second_cl(bug_id)
|
|
|
|
|
|
def create_second_cl(bug_id):
|
|
# Append 'ktfmt_update' at the end of a copy of the jar file to get
|
|
# a different sha1sum.
|
|
shutil.copyfile(jar_path, copy_path)
|
|
with open(copy_path, "a") as file_object:
|
|
file_object.write("ktfmt_update")
|
|
|
|
sha1sum = get_sha1sum(copy_path)
|
|
change_id = f"I{sha1sum}"
|
|
command = " ".join(sys.argv)
|
|
cl_message = textwrap.dedent(f"""
|
|
Format files with the upcoming version of ktfmt
|
|
|
|
This CL was generated automatically from the following command:
|
|
|
|
$ {command}
|
|
|
|
This CL formats all files already correctly formatted with the upcoming
|
|
version of ktfmt.
|
|
|
|
Bug: {bug_id}
|
|
Test: Presubmits
|
|
Change-Id: {change_id}
|
|
Merged-In: {change_id}
|
|
""")
|
|
|
|
run_cmd(["git", "add", "--all"])
|
|
run_cmd(["git", "commit", "-m", cl_message])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|