已合并
fix: 修复genop重复添加算子分类CMake目录 #4194
陈思创建于 7月22日
fix: 修复genop重复添加算子分类CMake目录 #4194
已合并
共 1 个文件变更+326-277
| @@ -1,277 +1,326 @@ | |||
| 1 | -# Copyright (c) 2025 Huawei Technologies Co., Ltd. | 1 | +# Copyright (c) 2025 Huawei Technologies Co., Ltd. |
| 2 | -# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | 2 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of |
| 3 | -# CANN Open Software License Agreement Version 2.0 (the "License"). | 3 | +# CANN Open Software License Agreement Version 2.0 (the "License"). |
| 4 | -# Please refer to the License for details. You may not use this file except in compliance with the License. | 4 | +# Please refer to the License for details. You may not use this file except in compliance with the License. |
| 5 | -# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | 5 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, |
| 6 | -# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | 6 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. |
| 7 | -# See LICENSE in the root of the software repository for the full text of the License. | 7 | +# See LICENSE in the root of the software repository for the full text of the License. |
| 8 | - | 8 | + |
| 9 | -import argparse | 9 | +import argparse |
| 10 | -import os | 10 | +import os |
| 11 | -import shutil | 11 | +import shutil |
| 12 | -import sys | 12 | +import sys |
| 13 | -import re | 13 | +import re |
| 14 | -import logging | 14 | +import logging |
| 15 | - | 15 | + |
| 16 | - | 16 | + |
| 17 | -class OpGenerator: | 17 | +class OpGenerator: |
| 18 | - """算子工程生成器""" | 18 | + """算子工程生成器""" |
| 19 | - | 19 | + |
| 20 | - def __init__(self, op_type, op_name, output_path, template_variant): | 20 | + def __init__(self, op_type, op_name, output_path, template_variant): |
| 21 | - self.op_type = op_type | 21 | + self.op_type = op_type |
| 22 | - self.op_name = op_name | 22 | + self.op_name = op_name |
| 23 | - self.output_path = output_path | 23 | + self.output_path = output_path |
| 24 | - self.template_name = "add_example" | 24 | + self.template_name = "add_example" |
| 25 | - | 25 | + |
| 26 | - self.script_dir = os.path.dirname(os.path.abspath(__file__)) | 26 | + self.script_dir = os.path.dirname(os.path.abspath(__file__)) |
| 27 | - if template_variant == "aicpu": | 27 | + if template_variant == "aicpu": |
| 28 | - self.template_dir = os.path.abspath(os.path.join(self.script_dir, 'template', 'add_example_aicpu')) | 28 | + self.template_dir = os.path.abspath( |
| 29 | - else: | 29 | + os.path.join(self.script_dir, "template", "add_example_aicpu") |
| 30 | - self.template_dir = os.path.abspath(os.path.join(self.script_dir, 'template', 'add_example')) | 30 | + ) |
| 31 | - | 31 | + else: |
| 32 | - self.dest_dir = os.path.abspath(os.path.join(self.output_path, self.op_type, self.op_name)) | 32 | + self.template_dir = os.path.abspath( |
| 33 | - | 33 | + os.path.join(self.script_dir, "template", "add_example") |
| 34 | - def run(self): | 34 | + ) |
| 35 | - """执行生成流程""" | 35 | + |
| 36 | - self._validate_inputs() | 36 | + self.category_dir = os.path.abspath( |
| 37 | - self._copy_template() | 37 | + os.path.join(self.output_path, self.op_type) |
| 38 | - self._rename_files() | 38 | + ) |
| 39 | - self._replace_content() | 39 | + self.category_existed = os.path.isdir(self.category_dir) |
| 40 | - self._update_cmake_chain() | 40 | + self.dest_dir = os.path.abspath( |
| 41 | - logging.info(f"成功为 {self.op_type}/{self.op_name} 创建算子工程!") | 41 | + os.path.join(self.output_path, self.op_type, self.op_name) |
| 42 | - logging.info(f"工程路径: {self.dest_dir}") | 42 | + ) |
| 43 | - | 43 | + |
| 44 | - def _validate_inputs(self): | 44 | + def run(self): |
| 45 | - """校验输入参数的有效性和安全性""" | 45 | + """执行生成流程""" |
| 46 | - if not self.op_type or not self.op_name: | 46 | + self._validate_inputs() |
| 47 | - raise ValueError("算子类型和算子名称均不能为空。") | 47 | + self._copy_template() |
| 48 | - | 48 | + self._rename_files() |
| 49 | - if not re.match(r"^[a-zA-Z0-9_]+$", self.op_type): | 49 | + self._replace_content() |
| 50 | - raise ValueError(f"算子类型 '{self.op_type}' 包含无效字符。只允许字母、数字和下划线。") | 50 | + self._update_cmake_chain() |
| 51 | - | 51 | + logging.info(f"成功为 {self.op_type}/{self.op_name} 创建算子工程!") |
| 52 | - if not re.match(r"^[a-zA-Z0-9_]+$", self.op_name): | 52 | + logging.info(f"工程路径: {self.dest_dir}") |
| 53 | - raise ValueError(f"算子名称 '{self.op_name}' 包含无效字符。只允许字母、数字和下划线。") | 53 | + |
| 54 | - | 54 | + def _validate_inputs(self): |
| 55 | - if os.path.exists(self.dest_dir): | 55 | + """校验输入参数的有效性和安全性""" |
| 56 | - raise FileExistsError(f"目标目录 '{self.dest_dir}' 已存在。") | 56 | + if not self.op_type or not self.op_name: |
| 57 | - | 57 | + raise ValueError("算子类型和算子名称均不能为空。") |
| 58 | - def _copy_template(self): | 58 | + |
| 59 | - """复制模板文件到目标目录""" | 59 | + if not re.match(r"^[a-zA-Z0-9_]+$", self.op_type): |
| 60 | - logging.info(f"使用模板在 '{self.dest_dir}' 创建算子工程...") | 60 | + raise ValueError( |
| 61 | - if not os.path.exists(self.template_dir): | 61 | + f"算子类型 '{self.op_type}' 包含无效字符。只允许字母、数字和下划线。" |
| 62 | - raise FileNotFoundError(f"找不到模板目录 '{self.template_dir}'。请确保模板目录存在。") | 62 | + ) |
| 63 | - | 63 | + |
| 64 | - try: | 64 | + if not re.match(r"^[a-zA-Z0-9_]+$", self.op_name): |
| 65 | - shutil.copytree(self.template_dir, self.dest_dir) | 65 | + raise ValueError( |
| 66 | - except OSError as e: | 66 | + f"算子名称 '{self.op_name}' 包含无效字符。只允许字母、数字和下划线。" |
| 67 | - raise OSError(f"复制模板文件失败: {e}") from e | 67 | + ) |
| 68 | - | 68 | + |
| 69 | - def _rename_files(self): | 69 | + if os.path.exists(self.dest_dir): |
| 70 | - """重命名文件和目录中的占位符""" | 70 | + raise FileExistsError(f"目标目录 '{self.dest_dir}' 已存在。") |
| 71 | - for root, dirs, files in os.walk(self.dest_dir, topdown=False): | 71 | + |
| 72 | - for name in files + dirs: | 72 | + def _copy_template(self): |
| 73 | - if self.template_name not in name: | 73 | + """复制模板文件到目标目录""" |
| 74 | - continue | 74 | + logging.info(f"使用模板在 '{self.dest_dir}' 创建算子工程...") |
| 75 | - | 75 | + if not os.path.exists(self.template_dir): |
| 76 | - old_path = os.path.join(root, name) | 76 | + raise FileNotFoundError( |
| 77 | - new_name = name.replace(self.template_name, self.op_name) | 77 | + f"找不到模板目录 '{self.template_dir}'。请确保模板目录存在。" |
| 78 | - new_path = os.path.join(root, new_name) | 78 | + ) |
| 79 | - try: | 79 | + |
| 80 | - os.rename(old_path, new_path) | 80 | + try: |
| 81 | - except OSError as e: | 81 | + shutil.copytree(self.template_dir, self.dest_dir) |
| 82 | - raise OSError(f"重命名 '{old_path}' 到 '{new_path}' 失败: {e}") from e | 82 | + except OSError as e: |
| 83 | - | 83 | + raise OSError(f"复制模板文件失败: {e}") from e |
| 84 | - @staticmethod | 84 | + |
| 85 | - def _create_category_cmake(dir_path): | 85 | + def _rename_files(self): |
| 86 | - """为新的分类目录创建CMakeLists.txt,使用与math/conversion/random相同的glob模式""" | 86 | + """重命名文件和目录中的占位符""" |
| 87 | - cmake_lines = [ | 87 | + for root, dirs, files in os.walk(self.dest_dir, topdown=False): |
| 88 | - "file(GLOB SUBDIRECTORIES LIST_DIRECTORIES true", | 88 | + for name in files + dirs: |
| 89 | - " RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)", | 89 | + if self.template_name not in name: |
| 90 | - "foreach(SUBDIR ${SUBDIRECTORIES})", | 90 | + continue |
| 91 | - " if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/CMakeLists.txt)", | 91 | + |
| 92 | - " add_subdirectory(${SUBDIR})", | 92 | + old_path = os.path.join(root, name) |
| 93 | - " endif()", | 93 | + new_name = name.replace(self.template_name, self.op_name) |
| 94 | - "endforeach()", | 94 | + new_path = os.path.join(root, new_name) |
| 95 | - ] | 95 | + try: |
| 96 | - cmake_file = os.path.join(dir_path, "CMakeLists.txt") | 96 | + os.rename(old_path, new_path) |
| 97 | - if not os.path.exists(cmake_file): | 97 | + except OSError as e: |
| 98 | - with open(cmake_file, 'w', encoding='utf-8') as f: | 98 | + raise OSError( |
| 99 | - f.write("\n".join(cmake_lines) + "\n") | 99 | + f"重命名 '{old_path}' 到 '{new_path}' 失败: {e}" |
| 100 | - logging.info(f"Created CMakeLists.txt in {dir_path}") | 100 | + ) from e |
| 101 | - | 101 | + |
| 102 | - @staticmethod | 102 | + @staticmethod |
| 103 | - def _add_to_ops_category_list(category_list_file, child): | 103 | + def _create_category_cmake(dir_path): |
| 104 | - """将新的分类添加到OPS_CATEGORY_LIST中""" | 104 | + """为新的分类目录创建CMakeLists.txt,使用与math/conversion/random相同的glob模式""" |
| 105 | - with open(category_list_file, 'r', encoding='utf-8') as f: | 105 | + cmake_lines = [ |
| 106 | - cat_content = f.read() | 106 | + "file(GLOB SUBDIRECTORIES LIST_DIRECTORIES true", |
| 107 | - updated = re.sub( | 107 | + " RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)", |
| 108 | - r'(set\s*\(\s*OPS_CATEGORY_LIST\s+[^)]*)', | 108 | + "foreach(SUBDIR ${SUBDIRECTORIES})", |
| 109 | - rf'\1\n "{child}"', | 109 | + " if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR}/CMakeLists.txt)", |
| 110 | - cat_content | 110 | + " add_subdirectory(${SUBDIR})", |
| 111 | - ) | 111 | + " endif()", |
| 112 | - with open(category_list_file, 'w', encoding='utf-8') as f: | 112 | + "endforeach()", |
| 113 | - f.write(updated) | 113 | + ] |
| 114 | - logging.info(f"Added '{child}' to OPS_CATEGORY_LIST in {category_list_file}") | 114 | + cmake_file = os.path.join(dir_path, "CMakeLists.txt") |
| 115 | - | 115 | + if not os.path.exists(cmake_file): |
| 116 | - @staticmethod | 116 | + with open(cmake_file, "w", encoding="utf-8") as f: |
| 117 | - def _check_included_in_cmake(content, cmake_file, child): | 117 | + f.write("\n".join(cmake_lines) + "\n") |
| 118 | - """检查child是否已被CMakeLists.txt包含,返回(already_included, ops_category_file_path)""" | 118 | + logging.info(f"Created CMakeLists.txt in {dir_path}") |
| 119 | - already_included = bool(re.search( | 119 | + |
| 120 | - rf'add_subdirectory\s*\(\s*{re.escape(child)}\s*[\s\)]', content | 120 | + @staticmethod |
| 121 | - )) | 121 | + def _add_to_ops_category_list(category_list_file, child): |
| 122 | - ops_category_file_path = None | 122 | + """将新的分类添加到OPS_CATEGORY_LIST中""" |
| 123 | - if already_included: | 123 | + with open(category_list_file, "r", encoding="utf-8") as f: |
| 124 | - return True, None | 124 | + cat_content = f.read() |
| 125 | - | 125 | + updated = re.sub( |
| 126 | - cmake_dir = os.path.dirname(cmake_file) | 126 | + r"(set\s*\(\s*OPS_CATEGORY_LIST\s+[^)]*)", rf'\1\n "{child}"', cat_content |
| 127 | - files_to_check = [(content, None)] | 127 | + ) |
| 128 | - for inc_match in re.finditer(r'include\s*\(\s*([^)\s]+)\s*\)', content): | 128 | + with open(category_list_file, "w", encoding="utf-8") as f: |
| 129 | - inc_path = inc_match.group(1) | 129 | + f.write(updated) |
| 130 | - if not os.path.isabs(inc_path): | 130 | + logging.info(f"Added '{child}' to OPS_CATEGORY_LIST in {category_list_file}") |
| 131 | - inc_path = os.path.join(cmake_dir, inc_path) | 131 | + |
| 132 | - if os.path.exists(inc_path): | 132 | + @staticmethod |
| 133 | - try: | 133 | + def _check_included_in_cmake(content, cmake_file, child): |
| 134 | - with open(inc_path, 'r', encoding='utf-8') as inc_f: | 134 | + """检查child是否已被CMakeLists.txt包含,返回(already_included, ops_category_file_path)""" |
| 135 | - files_to_check.append((inc_f.read(), inc_path)) | 135 | + already_included = bool( |
| 136 | - except (IOError, OSError): | 136 | + re.search(rf"add_subdirectory\s*\(\s*{re.escape(child)}\s*[\s\)]", content) |
| 137 | - pass | 137 | + ) |
| 138 | - | 138 | + ops_category_file_path = None |
| 139 | - for check_content, file_path in files_to_check: | 139 | + if already_included: |
| 140 | - match = re.search(r'set\s*\(\s*OPS_CATEGORY_LIST\s+([^)]+)\)', check_content) | 140 | + return True, None |
| 141 | - if not match: | 141 | + |
| 142 | - continue | 142 | + cmake_dir = os.path.dirname(cmake_file) |
| 143 | - categories = [c.strip('"') for c in match.group(1).split()] | 143 | + files_to_check = [(content, None)] |
| 144 | - if child in categories: | 144 | + for inc_match in re.finditer(r"include\s*\(\s*([^)\s]+)\s*\)", content): |
| 145 | - return True, None | 145 | + inc_path = inc_match.group(1) |
| 146 | - if file_path is not None: | 146 | + if not os.path.isabs(inc_path): |
| 147 | - ops_category_file_path = file_path | 147 | + inc_path = os.path.join(cmake_dir, inc_path) |
| 148 | - | 148 | + if os.path.exists(inc_path): |
| 149 | - return False, ops_category_file_path | 149 | + try: |
| 150 | - | 150 | + with open(inc_path, "r", encoding="utf-8") as inc_f: |
| 151 | - @staticmethod | 151 | + files_to_check.append((inc_f.read(), inc_path)) |
| 152 | - def _append_subdirectory(cmake_file, child): | 152 | + except (IOError, OSError): |
| 153 | - """在CMakeLists.txt末尾追加add_subdirectory(child)""" | 153 | + pass |
| 154 | - with open(cmake_file, 'a', encoding='utf-8') as f: | 154 | + |
| 155 | - f.write(f"\nadd_subdirectory({child})\n") | 155 | + for check_content, file_path in files_to_check: |
| 156 | - logging.info(f"Added add_subdirectory({child}) to {cmake_file}") | 156 | + match = re.search( |
| 157 | - | 157 | + r"set\s*\(\s*OPS_CATEGORY_LIST\s+([^)]+)\)", check_content |
| 158 | - def _update_cmake_chain(self): | 158 | + ) |
| 159 | - """在父目录的CMakeLists.txt中添加add_subdirectory,确保构建系统能找到新目录""" | 159 | + if not match: |
| 160 | - current = self.dest_dir | 160 | + continue |
| 161 | - project_root = os.path.abspath(self.output_path) | 161 | + categories = [c.strip('"') for c in match.group(1).split()] |
| 162 | - | 162 | + if child in categories: |
| 163 | - while True: | 163 | + return True, None |
| 164 | - parent = os.path.dirname(current) | 164 | + if file_path is not None: |
| 165 | - child = os.path.basename(current) | 165 | + ops_category_file_path = file_path |
| 166 | - parent_abs = os.path.abspath(parent) | 166 | + |
| 167 | - | 167 | + return False, ops_category_file_path |
| 168 | - cmake_file = os.path.join(parent, "CMakeLists.txt") | 168 | + |
| 169 | - if os.path.exists(cmake_file): | 169 | + @staticmethod |
| 170 | - with open(cmake_file, 'r', encoding='utf-8') as f: | 170 | + def _append_subdirectory(cmake_file, child): |
| 171 | - content = f.read() | 171 | + """在CMakeLists.txt末尾追加add_subdirectory(child)""" |
| 172 | - | 172 | + with open(cmake_file, "a", encoding="utf-8") as f: |
| 173 | - already_included, ops_category_file = self._check_included_in_cmake( | 173 | + f.write(f"\nadd_subdirectory({child})\n") |
| 174 | - content, cmake_file, child | 174 | + logging.info(f"Added add_subdirectory({child}) to {cmake_file}") |
| 175 | - ) | 175 | + |
| 176 | - uses_glob = bool(re.search(r'file\s*\(\s*GLOB', content)) | 176 | + def _update_cmake_chain(self): |
| 177 | - | 177 | + """在父目录的CMakeLists.txt中添加add_subdirectory,确保构建系统能找到新目录""" |
| 178 | - if not already_included and not uses_glob: | 178 | + current = self.dest_dir |
| 179 | - if ops_category_file: | 179 | + project_root = os.path.abspath(self.output_path) |
| 180 | - self._add_to_ops_category_list(ops_category_file, child) | 180 | + |
| 181 | - else: | 181 | + while True: |
| 182 | - self._append_subdirectory(cmake_file, child) | 182 | + parent = os.path.dirname(current) |
| 183 | - else: | 183 | + child = os.path.basename(current) |
| 184 | - if parent_abs != project_root and os.path.isdir(parent): | 184 | + parent_abs = os.path.abspath(parent) |
| 185 | - self._create_category_cmake(parent) | 185 | + |
| 186 | - | 186 | + cmake_file = os.path.join(parent, "CMakeLists.txt") |
| 187 | - if parent_abs == project_root: | 187 | + if os.path.exists(cmake_file): |
| 188 | - break | 188 | + with open(cmake_file, "r", encoding="utf-8") as f: |
| 189 | - | 189 | + content = f.read() |
| 190 | - current = parent | 190 | + |
| 191 | - | 191 | + already_included, ops_category_file = self._check_included_in_cmake( |
| 192 | - def _replace_content_in_file(self, file_path, replacements): | 192 | + content, cmake_file, child |
| 193 | - """Helper to replace content in a single file.""" | 193 | + ) |
| 194 | - try: | 194 | + uses_glob = bool(re.search(r"file\s*\(\s*GLOB", content)) |
| 195 | - with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: | 195 | + |
| 196 | - content = f.read() | 196 | + if not already_included and not uses_glob: |
| 197 | - except (IOError, OSError) as e: | 197 | + if ops_category_file: |
| 198 | - logging.warning(f"读取文件 '{file_path}' 失败: {e}") | 198 | + self._add_to_ops_category_list(ops_category_file, child) |
| 199 | - return | 199 | + else: |
| 200 | - | 200 | + self._append_subdirectory(cmake_file, child) |
| 201 | - original_content = content | 201 | + else: |
| 202 | - for old, new in replacements.items(): | 202 | + if parent_abs != project_root and os.path.isdir(parent): |
| 203 | - content = content.replace(old, new) | 203 | + self._create_category_cmake(parent) |
| 204 | - | 204 | + |
| 205 | - if content == original_content: | 205 | + # An existing category was already connected to its ancestors before |
| 206 | - return | 206 | + # this operator was generated. Only its own CMake file may need an |
| 207 | - | 207 | + # update; walking farther would risk adding the category twice when |
| 208 | - try: | 208 | + # an ancestor includes it indirectly (for example via a CMake list). |
| 209 | - with open(file_path, 'w', encoding='utf-8') as f: | 209 | + if self.category_existed and parent_abs == self.category_dir: |
| 210 | - f.write(content) | 210 | + break |
| 211 | - except (IOError, OSError) as e: | 211 | + |
| 212 | - logging.warning(f"写入文件 '{file_path}' 失败: {e}") | 212 | + if parent_abs == project_root: |
| 213 | - | 213 | + break |
| 214 | - def _replace_content(self): | 214 | + |
| 215 | - """替换文件内容中的占位符""" | 215 | + current = parent |
| 216 | - op_name_capitalized = ''.join(word.capitalize() for word in self.op_name.split('_')) | 216 | + |
| 217 | - template_name_capitalized = ''.join(word.capitalize() for word in self.template_name.split('_')) | 217 | + def _replace_content_in_file(self, file_path, replacements): |
| 218 | - | 218 | + """Helper to replace content in a single file.""" |
| 219 | - replacements = { | 219 | + try: |
| 220 | - self.template_name: self.op_name, | 220 | + with open(file_path, "r", encoding="utf-8", errors="ignore") as f: |
| 221 | - self.template_name.upper(): self.op_name.upper(), | 221 | + content = f.read() |
| 222 | - template_name_capitalized: op_name_capitalized, | 222 | + except (IOError, OSError) as e: |
| 223 | - "add_example": self.op_name, | 223 | + logging.warning(f"读取文件 '{file_path}' 失败: {e}") |
| 224 | - } | 224 | + return |
| 225 | - for root, _, files in os.walk(self.dest_dir): | 225 | + |
| 226 | - for file in files: | 226 | + original_content = content |
| 227 | - if file.endswith(('.pyc', '.pyo')): | 227 | + for old, new in replacements.items(): |
| 228 | - continue | 228 | + content = content.replace(old, new) |
| 229 | - | 229 | + |
| 230 | - file_path = os.path.join(root, file) | 230 | + if content == original_content: |
| 231 | - self._replace_content_in_file(file_path, replacements) | 231 | + return |
| 232 | - | 232 | + |
| 233 | - | 233 | + try: |
| 234 | -def execute(args): | 234 | + with open(file_path, "w", encoding="utf-8") as f: |
| 235 | - """根据命令行参数执行算子生成""" | 235 | + f.write(content) |
| 236 | - generator = OpGenerator( | 236 | + except (IOError, OSError) as e: |
| 237 | - op_type=args.op_type, | 237 | + logging.warning(f"写入文件 '{file_path}' 失败: {e}") |
| 238 | - op_name=args.op_name, | 238 | + |
| 239 | - output_path=args.output_path, | 239 | + def _replace_content(self): |
| 240 | - template_variant=args.template_variant | 240 | + """替换文件内容中的占位符""" |
| 241 | - ) | 241 | + op_name_capitalized = "".join( |
| 242 | - generator.run() | 242 | + word.capitalize() for word in self.op_name.split("_") |
| 243 | - | 243 | + ) |
| 244 | - | 244 | + template_name_capitalized = "".join( |
| 245 | -def register_parser(subparsers): | 245 | + word.capitalize() for word in self.template_name.split("_") |
| 246 | - """为 opgen 命令注册解析器。""" | 246 | + ) |
| 247 | - parser_opgen = subparsers.add_parser('opgen', help='生成项目骨架') | 247 | + |
| 248 | - parser_opgen.add_argument('--op_type', '-t', required=True, help='算子分类,例如 math') | 248 | + replacements = { |
| 249 | - parser_opgen.add_argument('--op_name', '-n', required=True, help='新算子的名称,例如 asinh') | 249 | + self.template_name: self.op_name, |
| 250 | - parser_opgen.add_argument('--output_path', '-p', default='.', help='生成工程的根路径') | 250 | + self.template_name.upper(): self.op_name.upper(), |
| 251 | - parser_opgen.add_argument( | 251 | + template_name_capitalized: op_name_capitalized, |
| 252 | - '--template_variant', '-v', | 252 | + "add_example": self.op_name, |
| 253 | - choices=['default', 'aicpu'], default='default', help='选择模板变种' | 253 | + } |
| 254 | - ) | 254 | + for root, _, files in os.walk(self.dest_dir): |
| 255 | - parser_opgen.set_defaults(func=execute) | 255 | + for file in files: |
| 256 | - | 256 | + if file.endswith((".pyc", ".pyo")): |
| 257 | - | 257 | + continue |
| 258 | -def main(): | 258 | + |
| 259 | - """主函数,用于独立执行""" | 259 | + file_path = os.path.join(root, file) |
| 260 | - logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s', stream=sys.stdout) | 260 | + self._replace_content_in_file(file_path, replacements) |
| 261 | - parser = argparse.ArgumentParser(description="生成项目骨架") | 261 | + |
| 262 | - | 262 | + |
| 263 | - parser.add_argument('--op_type', '-t', required=True, help='算子分类,例如 math') | 263 | +def execute(args): |
| 264 | - parser.add_argument('--op_name', '-n', required=True, help='新算子的名称,例如 asinh') | 264 | + """根据命令行参数执行算子生成""" |
| 265 | - parser.add_argument('--output_path', '-p', default='.', help='生成工程的根路径') | 265 | + generator = OpGenerator( |
| 266 | - parser.add_argument('--template_variant', '-v', choices=['default', 'aicpu'], default='default', help='选择模板变种') | 266 | + op_type=args.op_type, |
| 267 | - | 267 | + op_name=args.op_name, |
| 268 | - args = parser.parse_args() | 268 | + output_path=args.output_path, |
| 269 | - | 269 | + template_variant=args.template_variant, |
| 270 | - try: | 270 | + ) |
| 271 | - execute(args) | 271 | + generator.run() |
| 272 | - except Exception as e: | 272 | + |
| 273 | - logging.error(f"发生非预期的错误,退出。错误信息: {e}") | 273 | + |
| 274 | - sys.exit(1) | 274 | +def register_parser(subparsers): |
| 275 | - | 275 | + """为 opgen 命令注册解析器。""" |
| 276 | -if __name__ == "__main__": | 276 | + parser_opgen = subparsers.add_parser("opgen", help="生成项目骨架") |
| 277 | - main() | 277 | + parser_opgen.add_argument( |
| 278 | + "--op_type", "-t", required=True, help="算子分类,例如 math" | ||
| 279 | + ) | ||
| 280 | + parser_opgen.add_argument( | ||
| 281 | + "--op_name", "-n", required=True, help="新算子的名称,例如 asinh" | ||
| 282 | + ) | ||
| 283 | + parser_opgen.add_argument( | ||
| 284 | + "--output_path", "-p", default=".", help="生成工程的根路径" | ||
| 285 | + ) | ||
| 286 | + parser_opgen.add_argument( | ||
| 287 | + "--template_variant", | ||
| 288 | + "-v", | ||
| 289 | + choices=["default", "aicpu"], | ||
| 290 | + default="default", | ||
| 291 | + help="选择模板变种", | ||
| 292 | + ) | ||
| 293 | + parser_opgen.set_defaults(func=execute) | ||
| 294 | + | ||
| 295 | + | ||
| 296 | +def main(): | ||
| 297 | + """主函数,用于独立执行""" | ||
| 298 | + logging.basicConfig( | ||
| 299 | + level=logging.INFO, format="%(levelname)s: %(message)s", stream=sys.stdout | ||
| 300 | + ) | ||
| 301 | + parser = argparse.ArgumentParser(description="生成项目骨架") | ||
| 302 | + | ||
| 303 | + parser.add_argument("--op_type", "-t", required=True, help="算子分类,例如 math") | ||
| 304 | + parser.add_argument( | ||
| 305 | + "--op_name", "-n", required=True, help="新算子的名称,例如 asinh" | ||
| 306 | + ) | ||
| 307 | + parser.add_argument("--output_path", "-p", default=".", help="生成工程的根路径") | ||
| 308 | + parser.add_argument( | ||
| 309 | + "--template_variant", | ||
| 310 | + "-v", | ||
| 311 | + choices=["default", "aicpu"], | ||
| 312 | + default="default", | ||
| 313 | + help="选择模板变种", | ||
| 314 | + ) | ||
| 315 | + | ||
| 316 | + args = parser.parse_args() | ||
| 317 | + | ||
| 318 | + try: | ||
| 319 | + execute(args) | ||
| 320 | + except Exception as e: | ||
| 321 | + logging.error(f"发生非预期的错误,退出。错误信息: {e}") | ||
| 322 | + sys.exit(1) | ||
| 323 | + | ||
| 324 | + | ||
| 325 | +if __name__ == "__main__": | ||
| 326 | + main() | ||