Files
weixin_45205297 ffb839246f update
Signed-off-by: weixin_45205297 <linyuye@huawei.com>
2026-01-12 14:58:06 +08:00

328 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
#
# Copyright (c) 2025 Huawei Device Co., Ltd.
# 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.
#
import os
import subprocess
import time
import argparse
import shlex
import signal
import sys
import re
import tempfile
import shutil
import json
ROOT_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
def get_target(target):
if target.startswith("//"):
return target[2:]
else:
return target
def execute_build_command(command, use_shell=False):
process = subprocess.Popen(
command,
shell=use_shell,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT
)
while True:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
break
if output:
print(output.strip())
return_code = process.poll()
print(f"build finishreturn code is: {return_code}")
return return_code == 0
def monitor_file_and_stop(shell_script_path, target_file, shell_args=None,
use_shell=False):
if isinstance(shell_args, str):
command = [shell_script_path] + shlex.split(shell_args)
elif shell_args:
command = [shell_script_path] + shell_args
else:
command = [shell_script_path]
command += ["--build-only-gn", "--no-prebuilt-sdk"]
execute_build_command(command, use_shell=use_shell)
file_detected = False
while True:
if os.path.exists(target_file):
file_detected = True
break
time.sleep(0.5)
if file_detected:
if os.stat(target_file).st_size == 0:
print("No changes to the unittest detected, skipping compilation directly.")
sys.exit()
command.remove("--build-only-gn")
command.remove("--no-prebuilt-sdk")
command.extend(["--build-target", "precise_module_build"])
return execute_build_command(command, use_shell=use_shell)
return False
def process_changes():
change_info = read_json("change_info.json")
openharmony_fields = [v["name"] for v in change_info.values() if "name" in v]
change_files = []
file_operations = {
"added": lambda x: x,
"rename": lambda x: [item for pair in x for item in pair],
"modified": lambda x: x,
"deleted": lambda x: x
}
gn_files, c_files, h_files, other_files = [], [], [], []
file_type_map = {
"h": h_files,
"hh": h_files,
"hpp": h_files,
"gn": gn_files,
"gni": gn_files,
"c": c_files,
"cpp": c_files,
"cc": c_files,
"cxx": c_files,
"js": c_files,
"ets": c_files,
"in": h_files,
"cl": h_files,
"cppm": h_files,
"cuh": h_files,
"def": h_files,
"gch": h_files,
"glsl": h_files,
"h++": h_files,
"hhu": h_files,
"hlsl": h_files,
"i": h_files,
"icc": h_files,
"impl": h_files,
"inl": h_files,
"ipp": h_files,
"ixx": h_files,
"mpp": h_files,
"mxx": h_files,
"pch": h_files,
"protp": h_files,
"swig": h_files,
"template": h_files,
"tcc": h_files,
"thrift": h_files,
"tpp": h_files,
"wgsl": h_files,
}
for key, value in change_info.items():
changed_files = value.get("changed_file_list", {})
for operation, processor in file_operations.items():
if operation not in changed_files:
print(f"unknown file operation: {operation}")
continue
for modified_file in processor(changed_files[operation]):
target_list = file_type_map.get(get_file_extension(modified_file), other_files)
if target_list == gn_files:
patch_file_content = generate_patch(os.path.join(ROOT_PATH, key), value.get("name"))
print(patch_file_content)
review_content = parse_patch_for_buildgn(patch_file_content)
print("-"*100)
print(review_content)
print(ROOT_PATH)
print("-"*100)
if target_list is not None:
target_list.append("//" + os.path.join(key, modified_file))
modified_files = {
"h_file": h_files,
"c_file": c_files,
"gn_file": gn_files,
"other_file": other_files,
"gn_module":[]
}
file_path = os.path.abspath(__file__)
root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(file_path)))))
write_file = os.path.join(root_path, "modify_files.json")
with open(write_file, 'w') as json_file:
json.dump(modified_files, json_file, indent=4)
return (
[os.path.join(self.ace_root, f) for f in change_files],
openharmony_fields
)
def parse_patch_for_buildgn(content):
"""
解析patch文件,提取BUILD.gn文件的修改行号
特别注意:
1. 只提取以'+'开头的行(新增行)
2. 对于添加空行的情况,也要正确识别
"""
# 查找所有BUILD.gn文件的diff块
buildgn_diffs = re.findall(
r'diff --git a/(.*?BUILD\.gn.*?) b/(.*?BUILD\.gn.*?)\n(.*?)(?=\ndiff --git|\Z)',
content,
re.DOTALL
)
if not buildgn_diffs:
return None
results = []
for old_file, new_file, diff_content in buildgn_diffs:
changed_lines = []
current_line_num = 0
# 解析diff内容
lines = diff_content.split('\n')
for line in lines:
# 查找行号信息
if line.startswith('@@'):
# 提取新文件起始行号
match = re.search(r'\+(\d+)(?:,(\d+))?', line)
if match:
current_line_num = int(match.group(1))
continue
# 如果是新增行(但不包括文件头)
if line.startswith('+') and not line.startswith('++'):
changed_lines.append(str(current_line_num))
current_line_num += 1
continue
# 如果是上下文行或删除行,行号递增(但删除行不计入修改)
if line.startswith(' ') or line.startswith('-'):
if not line.startswith('---') and not line.startswith('--'):
current_line_num += 1
if changed_lines:
results.append(f"{new_file} {','.join(changed_lines)}")
return results
def generate_patch(part_path, part_name):
original_cwd = os.getcwd()
patch_file = ""
patch_content = ""
try:
os.chdir(part_path)
# 生成patch文件名
repo_name = part_name
patch_file = f"{repo_name}.patch"
cmd = f"git format-patch HEAD^ --stdout"
try:
result = subprocess.run(
cmd,
shell=True,
capture_output=True,
text=True,
check=True
)
# 将输出写入patch文件
if result.stdout.strip():
patch_content = result.stdout
with open(patch_file, 'w', encoding='utf-8') as pf:
pf.write(result.stdout)
else:
print(f" nothing changeno patch file")
except subprocess.CalledProcessError as e:
print(f" git diff command shibai: {e}")
print(f" generate patch error: {e.stderr}")
except Exception as e:
print(f" generate patch error: {e}")
finally:
os.chdir(original_cwd)
return patch_content
def get_file_extension(filename):
if '.' in filename:
return filename.split(".")[-1]
else:
return None
def read_json(path):
try:
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
return {}
if __name__ == "__main__":
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
add_help=False
)
parser.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS,
help='display help information and exit')
parser.add_argument('-s', '--script', required=True,
help='the path to the shell script to be executed')
parser.add_argument('-f', '--file', required=True,
help='the path of the target file to be monitored')
parser.add_argument('--use-shell', action='store_true',
help='execute commands using the shell (handle complex commands)')
parser.add_argument('shell_args', nargs=argparse.REMAINDER,
help='parameters passed to the shell script (after --)')
args = parser.parse_args()
process_changes()
shell_args = []
if args.shell_args:
try:
sep_index = args.shell_args.index("--")
shell_args = args.shell_args[sep_index + 1:]
except ValueError:
shell_args = args.shell_args
success = monitor_file_and_stop(
shell_script_path=args.script,
target_file=args.file,
shell_args=shell_args,
use_shell=args.use_shell,
)
exit(0 if success else 1)