已合并
create ops-fft #2
tianqiguang创建于 3月12日
create ops-fft #2
已合并
tianqiguang创建于 3月12日
75 个文件变更+18545-0
A.gitignore+69-0
@@ -0,0 +1,69 @@
1+# binary files
2+*.so
3+ 
4+cmake-build-debugindocker/
5+.DS_Store
6+# distribution and packaging
7+build/
8+build_out/
9+output/
10+dist/
11+*.egg-info/
12+*.egg
13+*.whl
14+ 
15+# vim cache
16+*.swp
17+ 
18+# python cache
19+*.pyc
20+__pycache__
21+MANIFEST.in
22+ 
23+# python ut/st
24+cov_report/
25+report/
26+data/
27+*.dump
28+!**/st/msopgen/simulator/*.dump
29+tf_plugin_model.pb
30+V*_onnx_plugin_model.onnx
31+ 
32+# protobuf
33+*.pb.h
34+*.pb.c
35+ 
36+# cann tmp
37+lib/
38+kernel_meta/
39+ 
40+# Jetbrain
41+.idea
42+cmake-build-debug
43+cmake-build-release
44+ 
45+# Visual Studio
46+.vs
47+ 
48+# Visual Studio Code
49+.vscode
50+ 
51+# UT report file
52+ut_report.txt
53+ 
54+# ascend package download dir
55+ascend_download
56+ 
57+# makeself
58+# makeself
59+ 
60+# tbetoolkits log files
61+tbetoolkits-*.log
62+*_result.csv
63+ 
64+/third_party
65+# version info (generated at build time)
66+version.info
67+ 
68+.gitcode
69+.claude
ACMakeLists.txt+77-0
@@ -0,0 +1,77 @@
1+##############################################################################
2+# ops-fft CMake 配置
3+##############################################################################
4+ 
5+cmake_minimum_required(VERSION 3.16)
6+ 
7+##############################################################################
8+# 一、基础配置
9+##############################################################################
10+ 
11+# 项目声明
12+project(ops_fft)
13+ 
14+# 包含变量和版本配置
15+include(cmake/func.cmake)
16+include(cmake/variables.cmake)
17+include(${CMAKE_CURRENT_SOURCE_DIR}/version.cmake)
18+ 
19+# 设置项目变量
20+set(OPS_FFT ops_fft)
21+set(PROJECT_VERSION "1.0.0")
22+ 
23+# 查找 ASC 编译器
24+find_package(ASC REQUIRED)
25+project(${OPS_FFT} VERSION ${PROJECT_VERSION} LANGUAGES ASC CXX)
26+ 
27+##############################################################################
28+# 二、环境初始化(封装)
29+##############################################################################
30+ 
31+include(cmake/init_env.cmake)
32+init_env() # 发现算子
33+ 
34+##############################################################################
35+# 三、核心构建流程
36+##############################################################################
37+ 
38+# 3.1 编译测试(可选)
39+option(BUILD_TESTING "Build tests" OFF)
40+ 
41+# 3.2 编译算子
42+add_subdirectory(src)
43+ 
44+# 3.3 编译测试
45+if(BUILD_TESTING)
46+ add_subdirectory(tests)
47+endif()
48+ 
49+# 3.4 打包配置
50+option(ENABLE_PACKAGE "Enable package generation" OFF)
51+if(ENABLE_PACKAGE)
52+ # 设置第三方库路径
53+ if(NOT CANN_3RD_LIB_PATH)
54+ set(CANN_3RD_LIB_PATH ${PROJECT_SOURCE_DIR}/third_party CACHE STRING "CANN third party lib path")
55+ endif()
56+ # 检查构建依赖
57+ check_pkg_build_deps(ops_fft)
58+ # 生成版本信息目标
59+ add_version_info_targets()
60+ # 打包
61+ include(cmake/package.cmake)
62+ pack_built_in()
63+endif()
64+ 
65+##############################################################################
66+# 四、构建摘要
67+##############################################################################
68+ 
69+message(STATUS "")
70+message(STATUS "==========================================")
71+message(STATUS " Build Summary")
72+message(STATUS "==========================================")
73+message(STATUS " Version : ${PROJECT_VERSION}")
74+message(STATUS " NPU : ${ASCEND_NPU_ARCH}")
75+message(STATUS " Operators : ${BUILD_OPERATORS}")
76+message(STATUS " Tests : ${BUILD_TESTING}")
77+message(STATUS "==========================================")
ACONTRIBUTING.md+101-0
@@ -0,0 +1,101 @@
1+# 贡献指南
2+ 
3+本项目欢迎广大开发者体验并参与贡献,在参与社区贡献之前。请参见[cann-community](https://gitcode.com/cann/community)了解行为准则,进行CLA协议签署,了解源码仓的贡献流程。
4+ 
5+开发者准备本地代码与提交PR时需要重点关注如下几点:
6+ 
7+1. 提交PR时,请按照PR模板仔细填写本次PR的业务背景、目的、方案等信息。
8+2. 若您的修改不是简单的bug修复,而是涉及到新增特性、新增接口、新增配置参数或者修改代码流程等,请务必先通过Issue进行方案讨论,以避免您的代码被拒绝合入。若您不确定本次修改是否可被归为"简单的bug修复",亦可通过提交Issue进行方案讨论。
9+ 
10+ 
11+开发者贡献场景主要包括:
12+ 
13+## 一、贡献新算子
14+ 
15+如果您有全新的算子希望基于 NPU 进行设计与实现,欢迎在 Issue 中提出您的想法与设计方案。完整的贡献流程如下:
16+ 
17+### 1. 新增 Issue,创建需求
18+ 
19+新建 `Requirement|需求建议` 类 Issue,并在其中说明新增算子的设计方案。
20+ 
21+Issue 需包含以下内容:
22+ 
23+- **背景信息**
24+- **价值/作用**
25+- **设计方案**
26+ 
27+同时,请在提交的 Issue 中评论`/assign @yourself` 认领该任务。
28+ 
29+### 2. 需求评审
30+ 
31+Sig组将指派Committer对您提交的 Issue 进行评审并给出修改意见。请在完成修改后,于 Issue 中@对应Committer。
32+ 
33+若需求被接纳,sig成员将为您分配合适的算子分类路径,以便您将贡献的算子提交至对应目录。
34+ 
35+### 3. 提交 PR
36+ 
37+算子交付件如下:
38+ 
39+```
40+src # 算子源码目录
41+├── ${op_name} # 算子名
42+│ ├── ${op_name}_kernel.cpp # 算子实现文件
43+│ ├── ${op_name}_host.cpp # Host侧代码
44+│ ├── arch35/ # Ascend950特有实现
45+│ │ └── ${op_name}_struct.h # 算子结构定义
46+│ └── tests
47+│ ├── test_${op_name}.cpp # 算子测试文件
48+├── CMakeLists.txt # 算子编译配置文件
49+├── README.md # 算子README文档
50+```
51+ 
52+代码上库要求:
53+- 代码交付件:需包含算子实现、算子测试文件、算子README文档(文档中说明算子提交人、功能、参数说明)
54+- 是否签署 CLA
55+- PR 是否已关联对应 Issue
56+- 代码是否符合《[C++ 编程规范](https://gitcode.com/cann/community/blob/master/contributor/coding-standards/C++%20Coding%20standards.md)》
57+- 代码是否编译通过
58+ 
59+### 4. CI门禁
60+ 
61+通过评论 `compile` 指令触发开源仓门禁,并依据 CI 检测结果进行修改,目前CI门禁包含以下检查项:
62+- 代码编译
63+- 静态检查(如涉及codecheck误报,请提交给sig成员屏蔽)
64+- UT测试
65+- 冒烟测试
66+ 
67+门禁通过后,请在关联的 Issue 中@指派的Committer。
68+ 
69+### 5. Committer检视
70+ 
71+Committer检视后将反馈检视意见,请完成所有修改后@指派的Committer。
72+ 
73+### 6. Maintainer检视合入
74+ 
75+Committer 检视通过后,标注 `/lgtm`标签。Maintainer 将在1天内进行最终审核,确认无问题后,将标注 `/approve` 标签合入PR。
76+ 
77+ 
78+## 二、算子Bug修复
79+ 
80+如果您在本项目中发现了某些算子Bug,希望对其进行修复,欢迎您新建Issue进行反馈和跟踪处理。
81+ 
82+您可以按照[提交Issue/处理Issue任务](https://gitcode.com/cann/community#提交Issue处理Issue任务)指引新建 `Bug-Report|缺陷反馈` 类Issue对Bug进行描述,然后在评论框中输入"/assign"或"/assign @yourself",将该Issue分配给您进行处理。
83+ 
84+## 三、算子优化
85+ 
86+如果您对本项目中某些算子实现有泛化性增强/性能优化思路,希望着手实现这些优化点,欢迎您对算子进行优化贡献。
87+ 
88+您可以按照[提交Issue/处理Issue任务](https://gitcode.com/cann/community#提交Issue处理Issue任务)指引新建 `Requirement|需求建议` 类Issue对优化点进行说明,并提供您的设计方案,
89+然后在评论框中输入"/assign"或"/assign @yourself",将该Issue分配给您进行跟踪优化。
90+ 
91+## 四、文档纠错
92+ 
93+如果您在本项目中发现某些算子文档描述错误,欢迎您新建Issue进行反馈和修复。
94+ 
95+您可以按照[提交Issue/处理Issue任务](https://gitcode.com/cann/community#提交Issue处理Issue任务)指引新建 `Documentation|文档反馈` 类Issue指出对应文档的问题,然后在评论框中输入"/assign"或"/assign @yourself",将该Issue分配给您纠正对应文档描述。
96+ 
97+## 五、帮助解决他人Issue
98+ 
99+如果社区中他人遇到的问题您有合适的解决方法,欢迎您在Issue中发表评论交流,帮助他人解决问题和痛点,共同优化易用性。
100+ 
101+如果对应Issue需要进行代码修改,您可以在Issue评论框中输入"/assign"或"/assign @yourself",将该Issue分配给您,跟踪协助解决问题。
AQUICKSTART.md+105-0
@@ -0,0 +1,105 @@
1+# 算子开发快速入门:基于ops-fft仓
2+ 
3+本指南旨在帮助你快速上手基于CANN和`ops-fft`算子仓的使用,最简化地完成环境安装、编译部署及算子运行。
4+ 
5+## 一、环境安装
6+ 
7+### 1. 有环境场景:Docker安装
8+ 
9+Docker安装环境以Atlas A2产品(910B)为例。
10+ 
11+**前提条件**
12+* **Docker环境**:宿主机已安装Docker引擎(版本1.11.2及以上)。
13+* **驱动与固件**:宿主机已安装昇腾NPU的[驱动与固件](https://www.hiascend.com/hardware/firmware-drivers/community?product=1&model=30&cann=8.0.RC3.alpha002&driver=1.0.26.alpha)Ascend HDK 24.1.0版本以上。安装指导详见《[CANN 软件安装指南](https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/850alpha002/softwareinst/instg/instg_0005.html?Mode=PmIns&OS=openEuler&Software=cannToolKit)》。
14+ > **注意**:使用`npu-smi info`查看对应的驱动与固件版本。
15+ 
16+#### 下载镜像
17+ 
18+拉取已预集成CANN软件包及`ops-fft`所需依赖的镜像。
19+ 
20+* **操作步骤**
21+ 1. 以root用户登录宿主机。
22+ 2. 执行拉取命令(请根据你的宿主机架构选择):
23+ * ARM架构:
24+ ```bash
25+ docker pull --platform=arm64 swr.cn-south-1.myhuaweicloud.com/ascendhub/cann:8.5.0-910b-ubuntu22.04-py3.10-ops
26+ ```
27+ * X86架构:
28+ ```bash
29+ docker pull --platform=amd64 swr.cn-south-1.myhuaweicloud.com/ascendhub/cann:8.5.0-910b-ubuntu22.04-py3.10-ops
30+ ```
31+ > **注意**:正常网速下,镜像下载时间约为5-10分钟。
32+ 
33+#### Docker运行
34+ 
35+请根据以下命令运行docker:
36+ 
37+```bash
38+docker run --name cann_container --device /dev/davinci0 --device /dev/davinci_manager --device /dev/devmm_svm --device /dev/hisi_hdc -v /usr/local/dcmi:/usr/local/dcmi -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ -v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info -v /etc/ascend_install.info:/etc/ascend_install.info -it swr.cn-south-1.myhuaweicloud.com/ascendhub/cann:8.5.0-910b-ubuntu22.04-py3.10-ops bash
39+```
40+ 
41+#### 检查环境
42+ 
43+进入容器后,验证环境和驱动是否正常。
44+ 
45+* **检查NPU设备**
46+ ```bash
47+ npu-smi info
48+ ```
49+* **检查CANN安装**
50+ ```bash
51+ cat /usr/local/Ascend/ascend-toolkit/latest/opp/version.info
52+ ```
53+ 
54+### 2. 无环境场景:WebIDE开发(建设中)
55+ 
56+对于无环境的用户,提供WebIDE开发方式,目前本方式正在建设中。
57+ 
58+## 二、编译部署
59+ 
60+### 1. 拉取ops-fft仓库代码
61+ 
62+```bash
63+git clone https://gitcode.com/cann/ops-fft.git
64+cd ops-fft
65+```
66+ 
67+### 2. 编译ops-fft算子包
68+ 
69+```bash
70+bash build.sh --pkg
71+```
72+ 
73+若提示如下信息,说明编译成功。
74+ 
75+```bash
76+Self-extractable archive "cann-ops-fft-1.0.0-linux-*.run" successfully created.
77+Build package success: build/cann-ops-fft-1.0.0-linux-*.run
78+```
79+ 
80+编译成功后,run包存放于项目根目录的build目录下。
81+ 
82+### 3. 安装ops-fft算子包
83+ 
84+```bash
85+./build/cann-ops-fft-*linux*.run
86+```
87+ 
88+ops-fft安装在`${ASCEND_HOME_PATH}/cann`路径中,`${ASCEND_HOME_PATH}`表示CANN软件安装目录。
89+ 
90+### 4. 配置环境变量
91+ 
92+```bash
93+source ${ASCEND_HOME_PATH}/cann/set_env.bash
94+```
95+ 
96+### 5. 运行测试
97+ 
98+```bash
99+bash build.sh --run
100+```
101+ 
102+## 三、更多帮助
103+ 
104+- [CANN 开发文档](https://www.hiascend.com/document)
105+- [Ascend C API 参考](https://hiascend.com/document/redirect/CannCommunityAscendCApi)
MREADME.md+214-0
@@ -1,2 +1,216 @@
1# ops-fft1# ops-fft
2 2 
3+## 🔥 最新动态
4+- [2026/03] 完成项目基础架构搭建,支持 Rfft1_d 算子实现和测试,支持一键编译、测试和打包。
5+- [2026/03] 建立完整的测试框架,支持单元测试、超时控制和自动化测试统计。
6+- [2026/03] 实现标准化的打包流程,生成 .run 安装包,支持 install/uninstall/upgrade 完整生命周期管理。
7+ 
8+## 🚀 概述
9+ 
10+ops-fft 是 [CANN](https://hiascend.com/software/cann) (Compute Architecture for Neural Networks)算子库中提供 FFT 类计算的基础算子库,采用模块化设计,支持灵活的算子开发和管理。
11+ 
12+### 主要特性
13+ 
14+-**模块化设计** - 支持动态添加算子模块,每个算子独立开发、编译和测试
15+-**标准 CMake 构建** - 跨平台编译支持,统一的构建流程
16+-**完整测试体系** - 基于自定义测试框架,支持自动化测试和超时控制
17+-**便捷打包** - 一键生成 .run 安装包,支持 install/uninstall/upgrade
18+-**版本管理** - 安装信息记录和版本追踪,支持升级管理
19+-**轻量高效** - 简洁的架构设计,避免过度工程化
20+ 
21+## 📝 版本配套
22+ 
23+本项目源码会跟随 CANN 软件版本发布,关于 CANN 软件版本与本项目标签的对应关系请参阅 [release 仓库](https://gitcode.com/cann/release-management) 中的相应版本说明。
24+ 
25+**当前版本:** v1.0.0 (2025-03-02)
26+ 
27+为确保您的源码定制开发顺利进行,请选择配套的 CANN 版本,使用 master 分支可能存在版本不匹配的风险。
28+ 
29+## ⚡️ 快速入门
30+ 
31+### 环境要求
32+ 
33+- CANN 8.0 及以上版本
34+- 支持 Ascend950、Ascend910B、Ascend910_93、Ascend910、Ascend310P、Ascend310B 等 SoC
35+- Linux x86_64/AArch64 平台
36+ 
37+### 环境配置
38+ 
39+```bash
40+# 设置 CANN 环境变量
41+source /usr/local/Ascend/cann/set_env.sh
42+ 
43+# 验证环境
44+echo $ASCEND_HOME_PATH
45+```
46+ 
47+### 编译与测试
48+ 
49+详细的 build.sh 参数说明请参考 [build 参数说明](docs/zh/context/build.md)。
50+ 
51+```bash
52+# 编译所有算子(默认 8 线程)
53+./build.sh
54+ 
55+# 编译指定算子
56+./build.sh --ops=rfft1_d
57+ 
58+# 编译并运行测试
59+./build.sh --run
60+ 
61+# 编译并打包成 .run 文件
62+./build.sh --pkg
63+ 
64+# 查看完整帮助信息
65+./build.sh --help
66+```
67+ 
68+### 安装
69+ 
70+```bash
71+# 标准安装(需要 root 权限)
72+sudo ./cann-950-ops-fft_9.0.0_linux-*.run
73+ 
74+# 查看安装包信息
75+./cann-950-ops-fft_9.0.0_linux-*.run --help
76+ 
77+# 安装到自定义路径
78+sudo ./cann-950-ops-fft_9.0.0_linux-*.run --install-path=/opt/ascend
79+ 
80+# 卸载
81+sudo ./cann-950-ops-fft_9.0.0_linux-*.run --uninstall
82+ 
83+# 升级
84+sudo ./cann-950-ops-fft_9.0.0_linux-*.run --upgrade
85+```
86+ 
87+## 📖 项目说明
88+ 
89+### 支持的算子
90+ 
91+当前支持的算子列表:
92+ 
93+| 算子名称 | 描述 | 状态 |
94+|---------|------|------|
95+| [Rfft1_d](src/rfft1_d/ | 一维实数FFT运算 | ✅ 已实现 |
96+ 
97+更多算子正在持续开发中...
98+ 
99+### SoC 支持矩阵
100+ 
101+| SoC 型号 | SOC_VERSION | 支持状态 |
102+|---------|-------------|---------|
103+| Ascend950 | ascend950dt_9595 | ✅ 默认支持 |
104+| Ascend910B | ascend910b3 | ✅ 支持 |
105+| Ascend910_93 | ascend910_93 | ✅ 支持 |
106+| Ascend910 | ascend910 | ✅ 支持 |
107+| Ascend310P | ascend310p | ✅ 支持 |
108+| Ascend310B | ascend310b | ✅ 支持 |
109+ 
110+## 🔍 目录结构
111+ 
112+```
113+ops-fft/
114+├── cmake/ # CMake 配置文件
115+│ ├── func.cmake # 公共函数(算子注册等)
116+│ ├── init_env.cmake # 环境初始化
117+│ ├── package.cmake # 打包配置
118+│ └── run_package.cmake # 打包脚本
119+├── include/ # 公共头文件
120+│ └── cann_ops_fft.h # API 头文件
121+├── scripts/ # 脚本目录
122+│ └── package/ # 打包相关脚本
123+├── src/ # 源代码目录
124+│ ├── rfft1_d/ # Rfft1_d 算子实现
125+│ │ ├── rfft1_d_host.cpp # Host 端实现
126+│ │ ├── rfft1_d_kernel.cpp # Kernel 端实现
127+│ │ ├── arch35/ # 架构特定代码(可选)
128+│ │ │ └── rfft1_d_struct.h # 数据结构定义(也可定义在 .cpp 中)
129+│ │ ├── tests/ # 算子测试
130+│ │ │ ├── rfft1_d_test.h
131+│ │ │ └── rfft1_d_test.cpp
132+│ │ └── CMakeLists.txt
133+│ ├── ... # 其他算子
134+│ └── CMakeLists.txt
135+├── tests/ # 测试框架
136+│ ├── test_common.h # 测试框架头文件
137+│ ├── test_common.cpp # 测试框架实现
138+│ ├── all_tests.cpp.in # 测试入口模板
139+│ └── CMakeLists.txt
140+├── build.sh # 编译脚本
141+├── CMakeLists.txt # 主 CMake 配置
142+├── version.info # 版本信息
143+└── README.md # 本文件
144+```
145+ 
146+**说明**
147+- Host 和 Kernel 可以合并为一个 `.cpp` 文件
148+- TilingData 可以定义在 `.cpp` 文件中,也可以独立为 `_struct.h`
149+- `arch35/` 目录是可选的,仅在需要区分不同 SOC 架构时使用
150+- 测试文件强烈推荐,但不是必需的
151+ 
152+## 🛠️ 开发指南
153+ 
154+### 添加新算子
155+ 
156+详细的算子开发指南请参考 [算子开发指南](docs/zh/develop/operator_development_guide.md),包括:
157+ 
158+- 完整的目录结构说明
159+- Host + Kernel 实现模板
160+- Tiling 数据结构定义
161+- 完整开发流程
162+ 
163+**快速开始**
164+ 
165+1. **创建目录**
166+```bash
167+mkdir -p src/my_op/arch35 (可选)
168+mkdir -p src/my_op/tests
169+```
170+ 
171+2. **编写算子实现**
172+创建 `src/my_op/my_op.cpp`,包含:
173+- Host 部分:对外接口、Tiling 计算、内存管理
174+- Kernel 部分:核函数实现
175+ 
176+3. **创建 CMakeLists.txt**
177+```cmake
178+register_operator(NAME my_op ARCH_DIR arch35)
179+```
180+ 
181+4. **编写测试**(推荐)
182+参考 [测试编写指南](docs/zh/develop/test_writing_guide.md)
183+ 
184+5. **编译验证**
185+```bash
186+./build.sh --ops=my_op --run
187+```
188+ 
189+完整示例参考 `src/rfft1_d/` 目录。
190+ 
191+### 编写测试
192+ 
193+ops-fft 提供了轻量级、自动化的测试框架。详细的测试编写指南请参考 [测试编写指南](docs/zh/develop/test_writing_guide.md),包括:
194+ 
195+- 测试框架特性
196+- 测试文件结构
197+- 核心宏和函数说明
198+- 完整编写步骤和示例
199+- 最佳实践和常见问题
200+ 
201+ 
202+ 
203+## 💬 相关信息
204+ 
205+- **许可证**: [CANN Open Software License Agreement Version 2.0](LICENSE)
206+- **安全声明**: [SECURITY.md](SECURITY.md)
207+- **贡献指南**: [CONTRIBUTING.md](CONTRIBUTING.md)
208+- **所属 SIG**: [CANN Community](https://gitcode.com/cann/community)
209+ 
210+## 🤝 联系我们
211+ 
212+本项目功能和文档正在持续更新和完善中,欢迎您关注最新版本。
213+ 
214+- **问题反馈**: 通过 [Issues](https://gitcode.com/cann/ops-fft/issues) 提交问题
215+- **社区互动**: 通过 [Discussions](https://gitcode.com/cann/ops-fft/discussions) 参与交流
216+- **技术专栏**: 通过 [Wiki](https://gitcode.com/cann/ops-fft/wiki) 获取技术文章
ASECURITY.md+58-0
@@ -0,0 +1,58 @@
1+# 安全声明
2+ 
3+## 运行用户建议
4+ 
5+基于安全性角度考虑,不建议使用root等管理员类型账户执行任何命令,遵循权限最小化原则。
6+ 
7+## 文件权限控制
8+ 
9+- 建议用户在主机(包括宿主机)及容器中设置运行系统umask值为0027及以上,保障新增文件夹默认最高权限为750,新增文件默认最高权限为640。
10+- 建议用户对个人隐私数据、商业资产、源文件和算子开发过程中保存的各类文件等敏感内容做好权限控制等安全措施。例如涉及本项目安装目录权限管控、输入公共数据文件权限管控,设定的权限建议参考[A-文件(夹)各场景权限管控推荐最大值](#A-文件(夹)各场景权限管控推荐最大值)。
11+- 算子运行时可能会缓存算子编译文件,存储在运行目录下的`kernel_meta_*`文件夹内,加快后续算子的调用速度,用户可根据需要自行对生成后的相关文件进行权限控制。
12+- 用户安装和使用过程需要做好权限控制,建议参考[A-文件(夹)各场景权限管控推荐最大值](#A-文件(夹)各场景权限管控推荐最大值)文件权限参考进行设置。
13+ 
14+## 构建安全声明
15+ 
16+在源码编译安装本项目时,需要您自行编译,编译过程中会生成一些中间文件,建议您在编译完成后,对中间文件做好权限控制,以保证文件安全。
17+ 
18+## 运行安全声明
19+ 
20+- 建议用户结合运行环境资源状况编写对应算子调用脚本。若算子调用脚本与资源状况不匹配,如生成输入数据或标杆计算结果使用空间超出内存容量限制、脚本在本地保存数据超过磁盘空间大小等情况,可能会引发错误并导致进程意外退出。
21+- 算子在运行异常时会退出进程并打印报错信息,建议根据报错提示定位具体错误原因,包括设定算子同步执行、查看日志文件等方式。
22+- 算子通过[PyTorch](https://gitee.com/ascend/pytorch)方式调用时,可能会因为版本不匹配导致运行错误,具体请参考[PyTorch安全声明](https://gitee.com/ascend/pytorch#%E5%AE%89%E5%85%A8%E5%A3%B0%E6%98%8E)。
23+ 
24+## 公网地址声明
25+ 
26+本项目代码中包含的公网地址声明如下所示:
27+ 
28+| 类型 | 开源代码地址 | 文件名 | 公网IP地址/公网URL地址/域名/邮箱地址/压缩文件地址 | 用途说明 |
29+| :---: | :---: | :--- | :--- | :--- |
30+| 依赖 | 不涉及 | cmake/third_party/opbase.cmake | https://gitcode.com/cann/opbase.git | 从gitcode下载opbase源码,作为编译依赖 |
31+ 
32+## 漏洞机制说明
33+ 
34+[漏洞管理](https://gitcode.com/cann/community/blob/master/security/security.md)
35+ 
36+## 附录
37+ 
38+### A-文件(夹)各场景权限管控推荐最大值
39+ 
40+| 类型 | Linux权限参考最大值 |
41+| :--- | :--- |
42+| 用户主目录 | 750(rwxr-x---) |
43+| 程序文件(含脚本文件、库文件等) | 550(r-xr-x---) |
44+| 程序文件目录 | 550(r-xr-x---) |
45+| 配置文件 | 640(rw-r-----) |
46+| 配置文件目录 | 750(rwxr-x---) |
47+| 日志文件(记录完毕或者已经归档) | 440(r--r-----) |
48+| 日志文件(正在记录) | 640(rw-r-----) |
49+| 日志文件目录 | 750(rwxr-x---) |
50+| Debug文件 | 640(rw-r-----) |
51+| Debug文件目录 | 750(rwxr-x---) |
52+| 临时文件目录 | 750(rwxr-x---) |
53+| 维护升级文件目录 | 770(rwxrwx---) |
54+| 业务数据文件 | 640(rw-r-----) |
55+| 业务数据文件目录 | 750(rwxr-x---) |
56+| 密钥组件、私钥、证书、密文文件目录 | 700(rwx-----) |
57+| 密钥组件、私钥、证书、加密密文 | 600(rw-------) |
58+| 加解密接口、加解密脚本 | 500(r-x------) |
Abuild.sh+521-0
@@ -0,0 +1,521 @@
1+#!/bin/bash
2+ 
3+##############################################################################
4+# ops-fft Build Script
5+#
6+# 使用方法:
7+# ./build.sh [OPTIONS]
8+#
9+# 选项:
10+# --ops=OP_LIST 指定要编译的算子列表 (逗号分隔)
11+# --run 编译后执行测试
12+# --pkg 编译并打包成 .run 文件
13+# --soc=SOC 指定目标 SoC 型号 (当前仅支持: Ascend950, 支持小写输入)
14+# -j[N] 编译线程数,默认为 8,例如: -j16
15+# --test-timeout=N 测试超时时间(秒),默认为 300
16+# -h, --help 显示帮助信息
17+#
18+# 行为说明:
19+# 无参数 编译所有算子,不执行测试
20+# --ops=rfft1_d 只编译 rfft1_d 算子,不执行测试
21+# --ops=rfft1_d,sub 编译 rfft1_d 和 irfft1_d 算子,不执行测试
22+# --run 编译所有算子,并执行所有算子的测试
23+# --ops=rfft1_d --run 编译 rfft1_d 算子,并执行 rfft1_d 算子的测试
24+# --ops=rfft1_d,sub --run 编译 rfft1_d,irfft1_d 算子,并执行这些算子的测试
25+# --pkg 编译所有算子并打包成 .run 文件 (默认 SoC: Ascend950)
26+# --ops=rfft1_d --pkg 编译 rfft1_d 算子并打包成 .run 文件
27+# --soc=ascend950 --pkg 为 Ascend950 芯片打包 (支持小写)
28+# -j16 使用 16 个线程编译
29+#
30+# 示例:
31+# ./build.sh # 编译所有算子 (默认 8 线程)
32+# ./build.sh --ops=rfft1_d # 只编译 rfft1_d 算子
33+# ./build.sh -j16 # 使用 16 线程编译所有算子
34+# ./build.sh --run # 编译所有算子并执行测试
35+# ./build.sh --ops=rfft1_d --run # 编译 rfft1_d 算子并执行测试
36+# ./build.sh --pkg # 编译所有算子并打包 (默认: Ascend950)
37+# ./build.sh --ops=rfft1_d --pkg # 编译 rfft1_d 算子并打包
38+# ./build.sh --soc=ascend950 --pkg # 为 Ascend950 打包 (支持小写输入)
39+# ./build.sh --test-timeout=600 --run # 运行测试 with 600s timeout
40+#
41+# 支持的 SoC 型号:
42+# Ascend950 (默认)
43+##############################################################################
44+ 
45+set -e
46+ 
47+# 颜色定义
48+RED='\033[0;31m'
49+GREEN='\033[0;32m'
50+YELLOW='\033[1;33m'
51+BLUE='\033[0;34m'
52+NC='\033[0m' # No Color
53+ 
54+# 默认参数
55+BUILD_OPERATORS="all"
56+RUN_TESTS=false
57+ENABLE_PACKAGE=false
58+SOC_NAME="Ascend950" # 默认 SoC 型号 (仅支持 Ascend950)
59+BUILD_DIR="build"
60+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
61+THREAD_NUM=8 # 默认编译线程数
62+CORE_NUMS=$(cat /proc/cpuinfo | grep "processor" | wc -l 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
63+TEST_TIMEOUT=300 # 默认测试超时时间(秒)
64+ 
65+# 设置 _ASCEND_INSTALL_PATH(优先级:ASCEND_INSTALL_PATH > ASCEND_HOME_PATH > 默认值)
66+if [ -n "$ASCEND_INSTALL_PATH" ]; then
67+ _ASCEND_INSTALL_PATH=$ASCEND_INSTALL_PATH
68+elif [ -n "$ASCEND_HOME_PATH" ]; then
69+ _ASCEND_INSTALL_PATH=$ASCEND_HOME_PATH
70+else
71+ _ASCEND_INSTALL_PATH="/usr/local/Ascend/cann"
72+fi
73+ 
74+# SoC 名称标准化函数(首字母大写,其余小写)
75+normalize_soc_name() {
76+ local soc="$1"
77+ # 转换为小写,然后首字母大写
78+ echo "${soc}" | sed 's/.*/\L&/; s/^./\U&/'
79+}
80+ 
81+# SoC 名称映射到完整的 SOC_VERSION(CANN ASC 编译器要求的格式)
82+get_soc_version() {
83+ local soc_name="$1"
84+ local soc_lower=$(echo "$soc_name" | tr '[:upper:]' '[:lower:]')
85+ 
86+ # 映射表:友好名称 -> CANN 期望的完整版本标识
87+ case "$soc_lower" in
88+ "ascend950")
89+ echo "ascend950dt_9595"
90+ ;;
91+ "ascend910b"|"ascend910_b")
92+ echo "ascend910b3"
93+ ;;
94+ "ascend910_93")
95+ echo "ascend910_93"
96+ ;;
97+ "ascend910")
98+ echo "ascend910"
99+ ;;
100+ "ascend310p"|"ascend310_p")
101+ echo "ascend310p"
102+ ;;
103+ "ascend310b"|"ascend310_b")
104+ echo "ascend310b"
105+ ;;
106+ *)
107+ # 如果没有映射,直接使用小写名称
108+ echo "$soc_lower"
109+ ;;
110+ esac
111+}
112+ 
113+# 检查 ASCEND 环境变量
114+check_ascend_env() {
115+ log_info "Checking ASCEND environment..."
116+ 
117+ if [ -z "${ASCEND_HOME_PATH}" ]; then
118+ log_error "ASCEND_HOME_PATH environment variable is not set!"
119+ echo ""
120+ echo "Please source the CANN environment script:"
121+ echo " source /usr/local/Ascend/cann/set_env.sh"
122+ echo ""
123+ echo "Then run build.sh again."
124+ exit 1
125+ fi
126+ 
127+ if [ ! -d "${ASCEND_HOME_PATH}" ]; then
128+ log_error "ASCEND_HOME_PATH directory does not exist: ${ASCEND_HOME_PATH}"
129+ exit 1
130+ fi
131+ 
132+ log_success "ASCEND_HOME_PATH: ${ASCEND_HOME_PATH}"
133+}
134+ 
135+# 日志函数(统一接口)
136+log() {
137+ local level=$1
138+ local msg=$2
139+ local color=""
140+ local prefix=""
141+ 
142+ case $level in
143+ info)
144+ color="${BLUE}"
145+ prefix="[INFO]"
146+ ;;
147+ success)
148+ color="${GREEN}"
149+ prefix="[SUCCESS]"
150+ ;;
151+ warning)
152+ color="${YELLOW}"
153+ prefix="[WARNING]"
154+ ;;
155+ error)
156+ color="${RED}"
157+ prefix="[ERROR]"
158+ ;;
159+ verbose)
160+ if [ "$VERBOSE" != true ]; then
161+ return
162+ fi
163+ color="${BLUE}"
164+ prefix="[VERBOSE]"
165+ ;;
166+ *)
167+ color="${NC}"
168+ prefix="[LOG]"
169+ ;;
170+ esac
171+ 
172+ echo -e "${color}${prefix}${NC} ${msg}"
173+}
174+ 
175+# 兼容性别名
176+log_info() { log info "$1"; }
177+log_success() { log success "$1"; }
178+log_warning() { log warning "$1"; }
179+log_error() { log error "$1"; }
180+log_verbose() { log verbose "$1"; }
181+ 
182+# 显示帮助信息
183+show_help() {
184+ cat << EOF
185+Usage: $(basename "$0") [OPTIONS]
186+ 
187+Options:
188+ --ops=OP_LIST Specify operators to build (comma-separated)
189+ --run Run tests after build
190+ --pkg Build package (.run file)
191+ --soc=SOC Target SoC model (default: Ascend950, case-insensitive)
192+ -j[N] Number of compile threads (default: 8), e.g., -j16
193+ --test-timeout=N Test timeout in seconds (default: 300)
194+ -h, --help Show this help message
195+ 
196+Supported SoC models:
197+ Ascend950 (dav-3510, default)
198+ Ascend910B (dav-2201)
199+ Ascend910_93 (dav-2201)
200+ Ascend910 (dav-2101)
201+ Ascend310P (dav-2101)
202+ 
203+Examples:
204+ $(basename "$0") # Build all operators (default 8 threads)
205+ $(basename "$0") --ops=rfft1_d # Build only 'rfft1_d' operator
206+ $(basename "$0") --ops=rfft1_d,sub # Build 'rfft1_d' and 'irfft1_d' operators
207+ $(basename "$0") -j16 # Build with 16 threads
208+ $(basename "$0") --run # Build all and run tests
209+ $(basename "$0") --ops=rfft1_d --run # Build 'rfft1_d' and run tests
210+ $(basename "$0") --pkg # Build package (default SoC: Ascend950)
211+ $(basename "$0") --ops=rfft1_d --pkg # Build 'rfft1_d' and package
212+ $(basename "$0") --soc=Ascend950 --pkg # Build package for Ascend950
213+ $(basename "$0") --soc=ascend950 --pkg # Build package (lowercase also works)
214+ $(basename "$0") --test-timeout=600 --run # Run tests with 600s timeout
215+ 
216+EOF
217+}
218+ 
219+# 解析逗号分隔的算子列表
220+parse_operators() {
221+ local ops_str=$1
222+ if [ "$ops_str" = "all" ]; then
223+ echo "all"
224+ else
225+ IFS=',' read -ra OPS <<< "$ops_str"
226+ printf '%s\n' "${OPS[@]}"
227+ fi
228+}
229+ 
230+# 获取所有可用的算子
231+get_available_operators() {
232+ find "$SCRIPT_DIR/src" -mindepth 1 -maxdepth 1 -type d -printf "%f\n" 2>/dev/null | sort
233+}
234+ 
235+# 验证指定的算子是否存在
236+validate_operators() {
237+ local ops=($1)
238+ local available_ops=($(get_available_operators))
239+ local invalid_ops=()
240+ 
241+ for op in "${ops[@]}"; do
242+ local found=false
243+ for avail_op in "${available_ops[@]}"; do
244+ if [ "$op" = "$avail_op" ]; then
245+ found=true
246+ break
247+ fi
248+ done
249+ if [ "$found" = false ]; then
250+ invalid_ops+=("$op")
251+ fi
252+ done
253+ 
254+ if [ ${#invalid_ops[@]} -gt 0 ]; then
255+ log_error "Invalid operators: ${invalid_ops[*]}"
256+ log_info "Available operators: ${available_ops[*]}"
257+ exit 1
258+ fi
259+}
260+ 
261+# 清理构建目录
262+clean_build() {
263+ log_info "Cleaning build directory..."
264+ if [ -d "$BUILD_DIR" ]; then
265+ rm -rf "$BUILD_DIR"
266+ log_success "Build directory cleaned"
267+ else
268+ log_warning "Build directory does not exist, nothing to clean"
269+ fi
270+}
271+ 
272+# 构建项目
273+build_project() {
274+ log_info "Starting build..."
275+ 
276+ # 创建构建目录
277+ mkdir -p "$BUILD_DIR"
278+ cd "$BUILD_DIR"
279+ 
280+ # 配置CMake
281+ log_info "Configuring CMake..."
282+ local cmake_args=()
283+ 
284+ if [ "$BUILD_OPERATORS" != "all" ]; then
285+ cmake_args+=(-DENABLED_OPERATORS="${BUILD_OPERATORS}")
286+ log_info "Enabled operators: ${BUILD_OPERATORS}"
287+ else
288+ log_info "All operators enabled"
289+ fi
290+ 
291+ # 如果需要运行测试,启用测试编译
292+ if [ "$RUN_TESTS" = true ]; then
293+ cmake_args+=(-DBUILD_TESTING=ON)
294+ log_info "Test compilation: ENABLED"
295+ else
296+ cmake_args+=(-DBUILD_TESTING=OFF)
297+ log_info "Test compilation: DISABLED"
298+ fi
299+ 
300+ # 如果需要打包,启用打包
301+ if [ "$ENABLE_PACKAGE" = true ]; then
302+ cmake_args+=(-DENABLE_PACKAGE=ON)
303+ log_info "Package generation: ENABLED"
304+ fi
305+ 
306+ # 传递 SoC 型号和 ASCEND 路径
307+ # SOC_VERSION: ASC 编译器期望简化格式(如 ascend950,不是 ascend950dt_9595)
308+ # ASCEND_SOC: init_env.cmake 用于映射 NPU 架构
309+ SOC_NAME_LOWER=$(echo "${SOC_NAME}" | tr '[:upper:]' '[:lower:]')
310+ cmake_args+=(-DSOC_VERSION="${SOC_NAME_LOWER}")
311+ cmake_args+=(-DASCEND_SOC="${SOC_NAME}")
312+ cmake_args+=(-DASCEND_HOME_PATH="${ASCEND_HOME_PATH}")
313+ log_info "Target SoC: ${SOC_NAME}"
314+ log_info " -> SOC_VERSION: ${SOC_NAME_LOWER} (for ASC compiler)"
315+ log_info " -> ASCEND_SOC: ${SOC_NAME} (for NPU arch mapping)"
316+ log_info "ASCEND path: ${ASCEND_HOME_PATH}"
317+ 
318+ cmake "${cmake_args[@]}" ..
319+ 
320+ # 编译
321+ log_info "Compiling with ${THREAD_NUM} threads..."
322+ cmake --build . -j${THREAD_NUM}
323+ 
324+ if [ $? -eq 0 ]; then
325+ log_success "Build succeeded"
326+ else
327+ log_error "Build failed"
328+ exit 1
329+ fi
330+ 
331+ cd "$SCRIPT_DIR"
332+}
333+ 
334+# 打包
335+build_package() {
336+ log_info "Building package..."
337+ 
338+ cd "$BUILD_DIR"
339+ 
340+ # 运行 ctest package target
341+ log_info "Running CPack..."
342+ cpack
343+ 
344+ if [ $? -eq 0 ]; then
345+ log_success "Package created successfully"
346+ # 查找生成的 .run 文件
347+ local run_file=$(ls *.run 2>/dev/null | head -n 1)
348+ if [ -n "$run_file" ]; then
349+ log_success "Package file: $BUILD_DIR/$run_file"
350+ fi
351+ else
352+ log_error "Package creation failed"
353+ exit 1
354+ fi
355+ 
356+ cd "$SCRIPT_DIR"
357+}
358+ 
359+# 运行测试
360+run_tests() {
361+ log_info "Running tests (timeout: ${TEST_TIMEOUT}s)..."
362+ 
363+ cd "$BUILD_DIR" || {
364+ log_error "Build directory not found: $BUILD_DIR"
365+ exit 1
366+ }
367+ 
368+ # 检查测试可执行文件
369+ if [ ! -f "./all_ops_test" ]; then
370+ log_error "Test executable not found: ./all_ops_test"
371+ log_error "Please build the project first with: ./build.sh --run"
372+ exit 1
373+ fi
374+ 
375+ log_info "Found test executable, starting..."
376+ 
377+ # 临时禁用 set -e,手动处理错误
378+ set +e
379+ timeout -k 1s ${TEST_TIMEOUT}s ./all_ops_test 2>&1
380+ test_result=$?
381+ set -e
382+ 
383+ cd "$SCRIPT_DIR"
384+ 
385+ if [ $test_result -ge 124 ]; then
386+ log_error "Test timeout (${TEST_TIMEOUT}s exceeded)"
387+ exit 1
388+ elif [ $test_result -ne 0 ]; then
389+ log_error "Some tests failed (exit code: $test_result)"
390+ exit 1
391+ else
392+ log_success "All tests passed"
393+ fi
394+}
395+ 
396+# 解析命令行参数
397+parse_arguments() {
398+ while [[ $# -gt 0 ]]; do
399+ case $1 in
400+ --ops=*)
401+ # 提取等号后的值
402+ BUILD_OPERATORS="${1#*=}"
403+ shift
404+ ;;
405+ --run)
406+ RUN_TESTS=true
407+ shift
408+ ;;
409+ --pkg)
410+ ENABLE_PACKAGE=true
411+ shift
412+ ;;
413+ --soc=*)
414+ # 提取 SoC 型号并标准化(支持小写输入)
415+ SOC_INPUT="${1#*=}"
416+ SOC_NAME=$(normalize_soc_name "${SOC_INPUT}")
417+ shift
418+ ;;
419+ -j*)
420+ # 提取线程数
421+ if [[ "$1" == "-j" ]]; then
422+ # -j N 的形式
423+ THREAD_NUM="$2"
424+ shift 2
425+ else
426+ # -jN 的形式
427+ THREAD_NUM="${1#-j}"
428+ shift
429+ fi
430+ ;;
431+ --test-timeout=*)
432+ # 提取测试超时时间
433+ TEST_TIMEOUT="${1#*=}"
434+ shift
435+ ;;
436+ -h|--help)
437+ show_help
438+ exit 0
439+ ;;
440+ *)
441+ log_error "Unknown option: $1"
442+ echo ""
443+ show_help
444+ exit 1
445+ ;;
446+ esac
447+ done
448+}
449+ 
450+# 主函数
451+main() {
452+ echo "=========================================="
453+ echo " ops-fft Build Script"
454+ echo "=========================================="
455+ echo ""
456+ 
457+ parse_arguments "$@"
458+ 
459+ # 检查并调整线程数
460+ if [ "$THREAD_NUM" -gt "$CORE_NUMS" ]; then
461+ log_warning "Thread num $THREAD_NUM over core num $CORE_NUMS, adjust to $CORE_NUMS"
462+ THREAD_NUM=$CORE_NUMS
463+ fi
464+ 
465+ # ASCEND 环境检查(强制要求)
466+ check_ascend_env
467+ 
468+ # 验证算子
469+ if [ "$BUILD_OPERATORS" != "all" ]; then
470+ IFS=',' read -ra OPS <<< "$BUILD_OPERATORS"
471+ validate_operators "${OPS[*]}"
472+ fi
473+ 
474+ # 显示执行计划
475+ echo ""
476+ log_info "========== Build Plan =========="
477+ if [ "$BUILD_OPERATORS" = "all" ]; then
478+ log_info "Build operators: all"
479+ else
480+ log_info "Build operators: ${BUILD_OPERATORS}"
481+ fi
482+ 
483+ if [ "$RUN_TESTS" = true ]; then
484+ log_success "Run tests: YES"
485+ log_info "Test timeout: ${TEST_TIMEOUT}s"
486+ if [ "$BUILD_OPERATORS" != "all" ]; then
487+ log_info "Test operators: ${BUILD_OPERATORS}"
488+ else
489+ log_info "Test operators: all built operators"
490+ fi
491+ else
492+ log_info "Run tests: NO"
493+ fi
494+ 
495+ if [ "$ENABLE_PACKAGE" = true ]; then
496+ log_success "Build package: YES"
497+ log_info "Target SoC: ${SOC_NAME}"
498+ else
499+ log_info "Build package: NO"
500+ fi
501+ echo "=================================="
502+ echo ""
503+ 
504+ # 构建项目
505+ build_project
506+ 
507+ # 运行测试(如果指定)
508+ if [ "$RUN_TESTS" = true ]; then
509+ run_tests
510+ fi
511+ 
512+ # 打包(如果指定)
513+ if [ "$ENABLE_PACKAGE" = true ]; then
514+ build_package
515+ fi
516+ 
517+ log_success "All operations completed!"
518+}
519+ 
520+# 运行主函数
521+main "$@"
Acmake/func.cmake+260-0
@@ -0,0 +1,260 @@
1+##############################################################################
2+# func.cmake
3+#
4+# 公共函数集合
5+#
6+# 提供统一的算子注册和测试注册接口
7+##############################################################################
8+ 
9+##############################################################################
10+# 函数: register_operator
11+#
12+# 功能: 注册算子源文件到 ops_fft 动态库
13+#
14+# 参数:
15+# NAME - 算子名称 (必需)
16+# ARCH_DIR - 架构特定目录名 (可选,默认: arch35)
17+#
18+# 使用示例:
19+# register_operator(NAME add ARCH_DIR arch35)
20+#
21+# 说明:
22+# - 自动收集当前目录下所有 .cpp 文件
23+# - 自动收集 ARCH_DIR 目录下所有 .cpp 文件并设置为 ASC 语言
24+##############################################################################
25+function(register_operator)
26+ # 解析参数
27+ cmake_parse_arguments(ARG
28+ "" # 选项
29+ "NAME;ARCH_DIR" # 单值参数
30+ "" # 多值参数
31+ ${ARGN}
32+ )
33+ 
34+ # 必需参数检查
35+ if(NOT ARG_NAME)
36+ message(FATAL_ERROR "register_operator: NAME parameter is required")
37+ endif()
38+ 
39+ # 设置默认架构目录
40+ if(NOT ARG_ARCH_DIR)
41+ set(ARG_ARCH_DIR "arch35")
42+ endif()
43+ 
44+ # 算子名称转大写
45+ string(TOUPPER ${ARG_NAME} OP_UPPER)
46+ 
47+ # 算子源文件路径
48+ set(OP_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
49+ 
50+ # 收集当前目录下的所有 .cpp 文件(非递归)
51+ file(GLOB CPP_SOURCES "${OP_SOURCE_DIR}/*.cpp")
52+ 
53+ # 收集架构目录下的所有 .cpp 文件(非递归)
54+ file(GLOB ARCH_SOURCES "${OP_SOURCE_DIR}/${ARG_ARCH_DIR}/*.cpp")
55+ 
56+ # 合并所有源文件
57+ set(ALL_SOURCES ${CPP_SOURCES} ${ARCH_SOURCES})
58+ 
59+ message(STATUS " Registered operator: ${ARG_NAME}")
60+ if(CPP_SOURCES)
61+ message(STATUS " C++ Sources: ${CPP_SOURCES}")
62+ endif()
63+ if(ARCH_SOURCES)
64+ message(STATUS " ASC Sources: ${ARCH_SOURCES}")
65+ endif()
66+ 
67+ # 一次性将所有源文件添加到 ops_fft 库
68+ if(ALL_SOURCES)
69+ target_sources(${OPS_FFT} PRIVATE ${ALL_SOURCES})
70+ endif()
71+ 
72+ # 设置架构目录下源文件的语言属性为 ASC
73+ if(ARCH_SOURCES)
74+ message(STATUS " Set ASC language for: ${ARCH_SOURCES}")
75+ endif()
76+ 
77+ # 添加算子特定的包含目录
78+ target_include_directories(${OPS_FFT} PRIVATE
79+ ${OP_SOURCE_DIR}
80+ ${OP_SOURCE_DIR}/${ARG_ARCH_DIR}
81+ )
82+ 
83+ # 添加算子特定的编译定义
84+ target_compile_definitions(${OPS_FFT} PRIVATE
85+ ENABLE_OPERATOR_${OP_UPPER}=1
86+ )
87+endfunction()
88+ 
89+##############################################################################
90+# 函数: register_operator_test
91+#
92+# 功能: 注册算子测试到 CTest
93+#
94+# 参数:
95+# OP_NAME - 算子名称 (必需)
96+##############################################################################
97+function(register_operator_test OP_NAME)
98+ if(NOT BUILD_TESTING)
99+ return()
100+ endif()
101+ 
102+ # 测试可执行文件
103+ add_executable(${OP_NAME}_test tests/${OP_NAME}_test.cpp)
104+ 
105+ # 链接库
106+ target_link_libraries(${OP_NAME}_test
107+ PRIVATE
108+ ${OPS_FFT}
109+ ${ASCEND_HOME_PATH}/lib64/libascendcl.so
110+ ${ASCEND_HOME_PATH}/lib64/libacl_rt.so
111+ )
112+ 
113+ # 包含目录
114+ target_include_directories(${OP_NAME}_test PRIVATE
115+ ${CMAKE_SOURCE_DIR}/include
116+ ${CMAKE_CURRENT_SOURCE_DIR}
117+ ${ASCEND_HOME_PATH}/include
118+ )
119+ 
120+ # 编译定义
121+ string(TOUPPER ${OP_NAME} OP_UPPER)
122+ target_compile_definitions(${OP_NAME}_test PRIVATE ENABLE_OPERATOR_${OP_UPPER}=1)
123+ 
124+ # 添加到CTest
125+ add_test(NAME ${OP_NAME}_test COMMAND ${OP_NAME}_test)
126+ set_tests_properties(${OP_NAME}_test PROPERTIES
127+ LABELS "operator;${OP_NAME}"
128+ TIMEOUT 300
129+ )
130+ 
131+ message(STATUS " Configured test for: ${OP_NAME}")
132+endfunction()
133+ 
134+##############################################################################
135+# 版本和依赖管理函数
136+##############################################################################
137+ 
138+##############################################################################
139+# 宏: replace_cur_major_minor_ver
140+#
141+# 功能: 替换依赖版本变量中的 CUR_MAJOR_MINOR_VER 占位符
142+##############################################################################
143+macro(replace_cur_major_minor_ver)
144+ string(REPLACE CUR_MAJOR_MINOR_VER "${CANN_VERSION_${CANN_VERSION_CURRENT_PACKAGE}_VERSION_MAJOR_MINOR}" depend "${depend}")
145+endmacro()
146+ 
147+##############################################################################
148+# 函数: set_package
149+#
150+# 功能: 设置包名和版本号
151+#
152+# 参数:
153+# name - 包名称 (必需)
154+# VERSION - 版本号 (必需)
155+##############################################################################
156+function(set_package name)
157+ cmake_parse_arguments(VERSION "" "VERSION" "" ${ARGN})
158+ set(VERSION "${VERSION_VERSION}")
159+ if(NOT name)
160+ message(FATAL_ERROR "The name parameter is not set in set_package.")
161+ endif()
162+ if(NOT VERSION)
163+ message(FATAL_ERROR "The VERSION parameter is not set in set_package(${name}).")
164+ endif()
165+ string(REGEX MATCH "^([0-9]+\\.[0-9]+)" VERSION_MAJOR_MINOR "${VERSION}")
166+ list(APPEND CANN_VERSION_PACKAGES "${name}")
167+ set(CANN_VERSION_PACKAGES "${CANN_VERSION_PACKAGES}" PARENT_SCOPE)
168+ set(CANN_VERSION_CURRENT_PACKAGE "${name}" PARENT_SCOPE)
169+ set(CANN_VERSION_${name}_VERSION "${VERSION}" PARENT_SCOPE)
170+ set(CANN_VERSION_${name}_VERSION_MAJOR_MINOR "${VERSION_MAJOR_MINOR}" PARENT_SCOPE)
171+ set(CANN_VERSION_${name}_BUILD_DEPS PARENT_SCOPE)
172+ set(CANN_VERSION_${name}_RUN_DEPS PARENT_SCOPE)
173+endfunction()
174+ 
175+##############################################################################
176+# 函数: set_build_dependencies
177+#
178+# 功能: 设置构建依赖
179+#
180+# 参数:
181+# pkg_name - 依赖包名称 (必需)
182+# depend - 依赖版本信息 (必需)
183+##############################################################################
184+function(set_build_dependencies pkg_name depend)
185+ if(NOT CANN_VERSION_CURRENT_PACKAGE)
186+ message(FATAL_ERROR "The set_package must be invoked first.")
187+ endif()
188+ if(NOT pkg_name)
189+ message(FATAL_ERROR "The pkg_name parameter is not set in set_build_dependencies.")
190+ endif()
191+ if(NOT depend)
192+ message(FATAL_ERROR "The depend parameter is not set in set_build_dependencies.")
193+ endif()
194+ replace_cur_major_minor_ver()
195+ list(APPEND CANN_VERSION_${CANN_VERSION_CURRENT_PACKAGE}_BUILD_DEPS "${pkg_name}" "${depend}")
196+ set(CANN_VERSION_${CANN_VERSION_CURRENT_PACKAGE}_BUILD_DEPS "${CANN_VERSION_${CANN_VERSION_CURRENT_PACKAGE}_BUILD_DEPS}" PARENT_SCOPE)
197+endfunction()
198+ 
199+##############################################################################
200+# 函数: set_run_dependencies
201+#
202+# 功能: 设置运行时依赖
203+#
204+# 参数:
205+# pkg_name - 依赖包名称 (必需)
206+# depend - 依赖版本信息 (必需)
207+##############################################################################
208+function(set_run_dependencies pkg_name depend)
209+ if(NOT CANN_VERSION_CURRENT_PACKAGE)
210+ message(FATAL_ERROR "The set_package must be invoked first.")
211+ endif()
212+ if(NOT pkg_name)
213+ message(FATAL_ERROR "The pkg_name parameter is not set in set_run_dependencies.")
214+ endif()
215+ if(NOT depend)
216+ message(FATAL_ERROR "The depend parameter is not set in set_run_dependencies.")
217+ endif()
218+ replace_cur_major_minor_ver()
219+ list(APPEND CANN_VERSION_${CANN_VERSION_CURRENT_PACKAGE}_RUN_DEPS "${pkg_name}" "${depend}")
220+ set(CANN_VERSION_${CANN_VERSION_CURRENT_PACKAGE}_RUN_DEPS "${CANN_VERSION_${CANN_VERSION_CURRENT_PACKAGE}_RUN_DEPS}" PARENT_SCOPE)
221+endfunction()
222+ 
223+##############################################################################
224+# 函数: check_pkg_build_deps
225+#
226+# 功能: 检查构建依赖是否满足
227+#
228+# 参数:
229+# pkg_name - 包名称 (必需)
230+##############################################################################
231+function(check_pkg_build_deps pkg_name)
232+ execute_process(
233+ COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/scripts/check_build_dependencies.py "$ENV{ASCEND_HOME_PATH}" ${CANN_VERSION_${pkg_name}_BUILD_DEPS}
234+ RESULT_VARIABLE result
235+ )
236+ if(result)
237+ message(FATAL_ERROR "Check ${pkg_name} build dependencies failed!")
238+ endif()
239+endfunction()
240+ 
241+##############################################################################
242+# 函数: add_version_info_targets
243+#
244+# 功能: 添加版本信息生成目标
245+#
246+# 说明:
247+# - 为每个已注册的包生成 version.{pkg_name}.info 文件
248+# - 目标名格式为:version_{pkg_name}_info
249+##############################################################################
250+function(add_version_info_targets)
251+ foreach(pkg_name ${CANN_VERSION_PACKAGES})
252+ add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/version.${pkg_name}.info
253+ COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_version_info.py --output ${CMAKE_BINARY_DIR}/version.${pkg_name}.info
254+ "${CANN_VERSION_${pkg_name}_VERSION}" ${CANN_VERSION_${pkg_name}_RUN_DEPS}
255+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/version.cmake ${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate_version_info.py
256+ VERBATIM
257+ )
258+ add_custom_target(version_${pkg_name}_info ALL DEPENDS ${CMAKE_BINARY_DIR}/version.${pkg_name}.info)
259+ endforeach()
260+endfunction()
Acmake/init_env.cmake+86-0
@@ -0,0 +1,86 @@
1+##############################################################################
2+# 环境初始化模块
3+# 功能:检查环境、设置 NPU 架构、发现算子
4+##############################################################################
5+ 
6+macro(init_env)
7+ # 1. 检查 ASCEND 环境
8+ _check_ascend()
9+ 
10+ # 2. 设置 NPU 架构
11+ _setup_npu_arch()
12+ 
13+ # 3. 发现算子
14+ _discover_operators()
15+endmacro()
16+ 
17+##############################################################################
18+# 内部宏
19+##############################################################################
20+ 
21+# 1. 检查 ASCEND 路径
22+macro(_check_ascend)
23+ if(NOT EXISTS "${ASCEND_HOME_PATH}")
24+ message(FATAL_ERROR
25+ "✗ ASCEND_HOME_PATH not found: ${ASCEND_HOME_PATH}\n"
26+ " Fix: source /usr/local/Ascend/cann/set_env.sh"
27+ )
28+ endif()
29+ message(STATUS "✓ ASCEND: ${ASCEND_HOME_PATH}")
30+endmacro()
31+ 
32+# 2. 设置 NPU 架构(SoC 映射)
33+macro(_setup_npu_arch)
34+ # 映射表(注意:dav-3101 已改名为 dav-3510)
35+ set(SOC_TO_NPU_ARCH_MAP
36+ "Ascend950" "dav-3510"
37+ "Ascend910B" "dav-2201"
38+ "Ascend910_93" "dav-2201"
39+ "Ascend910" "dav-2101"
40+ "Ascend310P" "dav-2101"
41+ )
42+ 
43+ # 确定架构(优先级:直接指定 > SoC映射 > 默认)
44+ if(DEFINED ASCEND_NPU_ARCH AND NOT "${ASCEND_NPU_ARCH}" STREQUAL "")
45+ message(STATUS "✓ NPU: ${ASCEND_NPU_ARCH} (user)")
46+ elseif(DEFINED ASCEND_SOC)
47+ list(FIND SOC_TO_NPU_ARCH_MAP "${ASCEND_SOC}" IDX)
48+ if(IDX EQUAL -1)
49+ message(FATAL_ERROR "✗ Unknown SoC: ${ASCEND_SOC}")
50+ endif()
51+ math(EXPR IDX "${IDX} + 1")
52+ list(GET SOC_TO_NPU_ARCH_MAP ${IDX} ASCEND_NPU_ARCH)
53+ message(STATUS "✓ NPU: ${ASCEND_NPU_ARCH} (${ASCEND_SOC})")
54+ else()
55+ set(ASCEND_NPU_ARCH "dav-3510")
56+ message(STATUS "✓ NPU: ${ASCEND_NPU_ARCH} (default)")
57+ endif()
58+endmacro()
59+ 
60+# 3. 发现算子
61+macro(_discover_operators)
62+ # 扫描 src 目录
63+ file(GLOB OPS RELATIVE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/*)
64+ foreach(OP ${OPS})
65+ if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/src/${OP})
66+ list(APPEND OPERATOR_DIRS ${OP})
67+ endif()
68+ endforeach()
69+ list(SORT OPERATOR_DIRS)
70+ 
71+ # 解析用户指定(不强制清空,检查是否已定义)
72+ if(NOT DEFINED ENABLED_OPERATORS OR "${ENABLED_OPERATORS}" STREQUAL "")
73+ set(BUILD_OPERATORS ${OPERATOR_DIRS})
74+ message(STATUS "✓ Build: all (${OPERATOR_DIRS})")
75+ else()
76+ string(REPLACE "," ";" OP_LIST "${ENABLED_OPERATORS}")
77+ foreach(OP ${OP_LIST})
78+ list(FIND OPERATOR_DIRS ${OP} IDX)
79+ if(IDX EQUAL -1)
80+ message(FATAL_ERROR "✗ Unknown operator: ${OP}")
81+ endif()
82+ list(APPEND BUILD_OPERATORS ${OP})
83+ endforeach()
84+ message(STATUS "✓ Build: ${BUILD_OPERATORS}")
85+ endif()
86+endmacro()
Acmake/makeself_built_in.cmake+134-0
@@ -0,0 +1,134 @@
1+# ----------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# ----------------------------------------------------------------------------
10+# makeself_built_in.cmake - ops-fft 内置自解压打包脚本
11+ 
12+# 设置 makeself 路径
13+set(MAKESELF_EXE ${CPACK_MAKESELF_PATH}/makeself.sh)
14+set(MAKESELF_HEADER_EXE ${CPACK_MAKESELF_PATH}/makeself-header.sh)
15+ 
16+if(NOT MAKESELF_EXE)
17+ message(FATAL_ERROR "makeself not found!")
18+endif()
19+ 
20+# 创建临时安装目录
21+set(STAGING_DIR "${CPACK_CMAKE_BINARY_DIR}/_CPack_Packages/makeself_staging")
22+ 
23+# 先删除再创建
24+file(REMOVE_RECURSE "${STAGING_DIR}")
25+file(MAKE_DIRECTORY "${STAGING_DIR}")
26+ 
27+# 执行安装到临时目录
28+execute_process(
29+ COMMAND "${CMAKE_COMMAND}" --install "${CPACK_CMAKE_BINARY_DIR}" --prefix "${STAGING_DIR}"
30+ RESULT_VARIABLE INSTALL_RESULT
31+)
32+ 
33+if(NOT INSTALL_RESULT EQUAL 0)
34+ message(FATAL_ERROR "Installation to staging directory failed: ${INSTALL_RESULT}")
35+endif()
36+ 
37+# 生成安装配置文件
38+set(CSV_OUTPUT ${CPACK_CMAKE_BINARY_DIR}/filelist.csv)
39+execute_process(
40+ COMMAND python3 ${CPACK_CMAKE_SOURCE_DIR}/scripts/package/package.py --pkg_name ops_fft --chip_name ${CPACK_SOC} --os_arch linux-${CPACK_ARCH}
41+ WORKING_DIRECTORY ${CPACK_CMAKE_BINARY_DIR}
42+ OUTPUT_VARIABLE result
43+ ERROR_VARIABLE error
44+ RESULT_VARIABLE code
45+ OUTPUT_STRIP_TRAILING_WHITESPACE
46+)
47+ 
48+message(STATUS "package.py exit code: ${code}")
49+if(NOT code EQUAL 0)
50+ message(WARNING "Filelist generation failed: ${result}")
51+else()
52+ message(STATUS "Filelist generated successfully")
53+endif()
54+ 
55+if(NOT EXISTS ${CSV_OUTPUT})
56+ message(WARNING "Output file not created: ${CSV_OUTPUT}")
57+endif()
58+ 
59+# 复制版本信息
60+set(SCENE_OUT_PUT ${CPACK_CMAKE_BINARY_DIR}/scene.info)
61+set(OPS_FFT_VERSION_OUT_PUT ${CPACK_CMAKE_BINARY_DIR}/version.ops_fft.info)
62+set(OPS_FFT_VERSION_H ${CPACK_CMAKE_BINARY_DIR}/ops_fft_version.h)
63+ 
64+if(EXISTS ${SCENE_OUT_PUT})
65+ configure_file(
66+ ${SCENE_OUT_PUT}
67+ ${STAGING_DIR}/share/info/ops_fft/
68+ COPYONLY
69+ )
70+endif()
71+ 
72+if(EXISTS ${CSV_OUTPUT})
73+ configure_file(
74+ ${CSV_OUTPUT}
75+ ${STAGING_DIR}/share/info/ops_fft/script/
76+ COPYONLY
77+ )
78+endif()
79+ 
80+if(EXISTS ${OPS_FFT_VERSION_OUT_PUT})
81+ configure_file(
82+ ${OPS_FFT_VERSION_OUT_PUT}
83+ ${STAGING_DIR}/share/info/ops_fft/
84+ COPYONLY
85+ )
86+endif()
87+ 
88+if(EXISTS ${OPS_FFT_VERSION_H})
89+ configure_file(
90+ ${OPS_FFT_VERSION_H}
91+ ${STAGING_DIR}/share/info/ops_fft/
92+ COPYONLY
93+ )
94+endif()
95+ 
96+# makeself打包
97+file(STRINGS ${CPACK_CMAKE_BINARY_DIR}/makeself.txt script_output)
98+string(REPLACE " " ";" makeself_param_string "${script_output}")
99+string(REGEX MATCH "cann.*\\.run" package_name "${makeself_param_string}")
100+ 
101+list(LENGTH makeself_param_string LIST_LENGTH)
102+math(EXPR INSERT_INDEX "${LIST_LENGTH} - 2")
103+list(INSERT makeself_param_string ${INSERT_INDEX} "${STAGING_DIR}")
104+ 
105+message(STATUS "script output: ${script_output}")
106+message(STATUS "makeself: ${makeself_param_string}")
107+message(STATUS "package: ${package_name}")
108+ 
109+execute_process(
110+ COMMAND bash ${MAKESELF_EXE}
111+ --header ${MAKESELF_HEADER_EXE}
112+ --help-header share/info/ops_fft/script/help.info
113+ ${makeself_param_string} share/info/ops_fft/script/install.sh
114+ WORKING_DIRECTORY ${STAGING_DIR}
115+ RESULT_VARIABLE EXEC_RESULT
116+ ERROR_VARIABLE EXEC_ERROR
117+)
118+ 
119+if(NOT EXEC_RESULT EQUAL 0)
120+ message(FATAL_ERROR "makeself packaging failed: ${EXEC_ERROR}")
121+endif()
122+ 
123+# 移动最终包到输出目录
124+execute_process(
125+ COMMAND mkdir -p ${CPACK_PACKAGE_DIRECTORY}
126+)
127+execute_process(
128+ COMMAND mv ${STAGING_DIR}/${package_name} ${CPACK_PACKAGE_DIRECTORY}/
129+)
130+execute_process(
131+ COMMAND echo "Build package success: ${CPACK_PACKAGE_DIRECTORY}/${package_name}"
132+)
133+ 
134+message(STATUS "Package created successfully: ${CPACK_PACKAGE_DIRECTORY}/${package_name}")
Acmake/package.cmake+123-0
@@ -0,0 +1,123 @@
1+# ----------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# ----------------------------------------------------------------------------
10+# ops-fft 打包函数 (Built-in 包)
11+ 
12+# 下载 makeself 工具
13+include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/third_party/makeself-fetch.cmake)
14+ 
15+function(pack_built_in)
16+ message(STATUS "============================================")
17+ message(STATUS "Packing built-in ops_fft package...")
18+ message(STATUS " SOC: ${SOC_VERSION}")
19+ message(STATUS " Architecture: ${ARCH}")
20+ message(STATUS "============================================")
21+ 
22+ # 1. 安装脚本文件 (与 ops-math 保持一致)
23+ set(script_prefix ${CMAKE_SOURCE_DIR}/scripts/package/ops_fft/scripts)
24+ install(DIRECTORY ${script_prefix}/
25+ DESTINATION share/info/ops_fft/script
26+ FILE_PERMISSIONS
27+ OWNER_READ OWNER_WRITE OWNER_EXECUTE
28+ GROUP_READ GROUP_EXECUTE
29+ WORLD_READ WORLD_EXECUTE
30+ DIRECTORY_PERMISSIONS
31+ OWNER_READ OWNER_WRITE OWNER_EXECUTE
32+ GROUP_READ GROUP_EXECUTE
33+ WORLD_READ WORLD_EXECUTE
34+ REGEX "(setenv|prereq_check)\.(bash|fish|csh)" EXCLUDE
35+ )
36+ 
37+ # 2. 通用脚本文件 (与 ops-math 保持一致)
38+ set(SCRIPTS_FILES
39+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/check_version_required.awk
40+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/common_func.inc
41+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/common_interface.sh
42+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/common_interface.csh
43+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/common_interface.fish
44+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/version_compatiable.inc
45+ )
46+ install(FILES ${SCRIPTS_FILES}
47+ DESTINATION share/info/ops_fft/script
48+ OPTIONAL
49+ )
50+ 
51+ # 3. 打包相关脚本
52+ set(COMMON_FILES
53+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/install_common_parser.sh
54+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/common_func_v2.inc
55+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/common_installer.inc
56+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/script_operator.inc
57+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/version_cfg.inc
58+ )
59+ set(PACKAGE_FILES
60+ ${COMMON_FILES}
61+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/multi_version.inc
62+ )
63+ set(LATEST_MANGER_FILES
64+ ${COMMON_FILES}
65+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/common_func.inc
66+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/version_compatiable.inc
67+ ${CMAKE_SOURCE_DIR}/scripts/package/common/sh/check_version_required.awk
68+ )
69+ set(CONF_FILES
70+ ${CMAKE_SOURCE_DIR}/scripts/package/common/cfg/path.cfg
71+ )
72+ 
73+ # 4. 安装版本信息
74+ install(FILES ${CMAKE_BINARY_DIR}/version.ops_fft.info
75+ DESTINATION share/info/ops_fft
76+ RENAME version.info
77+ )
78+ 
79+ # 5. 安装配置文件和脚本
80+ install(FILES ${CONF_FILES}
81+ DESTINATION ops_fft/conf
82+ OPTIONAL
83+ )
84+ install(FILES ${PACKAGE_FILES}
85+ DESTINATION share/info/ops_fft/script
86+ OPTIONAL
87+ )
88+ install(FILES ${LATEST_MANGER_FILES}
89+ DESTINATION latest_manager
90+ OPTIONAL
91+ )
92+ 
93+ # 6. 安装 latest_manager 脚本
94+ if(EXISTS ${CMAKE_SOURCE_DIR}/scripts/package/latest_manager/scripts/)
95+ install(DIRECTORY ${CMAKE_SOURCE_DIR}/scripts/package/latest_manager/scripts/
96+ DESTINATION latest_manager
97+ OPTIONAL
98+ )
99+ endif()
100+ 
101+ # 7. CPack 配置
102+ set(CPACK_PACKAGE_NAME "cann-${SOC_VERSION}-ops-fft")
103+ set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
104+ set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}_linux-${ARCH}")
105+ set(CPACK_INSTALL_PREFIX "/")
106+ 
107+ set(CPACK_CMAKE_SOURCE_DIR "${CMAKE_SOURCE_DIR}")
108+ set(CPACK_CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}")
109+ set(CPACK_CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
110+ set(CPACK_CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
111+ set(CPACK_MAKESELF_PATH "${MAKESELF_PATH}")
112+ set(CPACK_SOC "${SOC_VERSION}")
113+ set(CPACK_ARCH "${ARCH}")
114+ set(CPACK_SET_DESTDIR ON)
115+ 
116+ set(CPACK_GENERATOR External)
117+ set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_SOURCE_DIR}/cmake/makeself_built_in.cmake")
118+ set(CPACK_EXTERNAL_ENABLE_STAGING true)
119+ set(CPACK_PACKAGE_DIRECTORY "${CMAKE_INSTALL_PREFIX}")
120+ 
121+ message(STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}")
122+ include(CPack)
123+endfunction()
Acmake/third_party/makeself-fetch.cmake+58-0
@@ -0,0 +1,58 @@
1+# ----------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# ----------------------------------------------------------------------------
10+# makeself 工具下载
11+ 
12+set(MAKESELF_NAME "makeself")
13+set(MAKESELF_PATH ${CANN_3RD_LIB_PATH}/makeself)
14+set(MAKESELF_VERSION_PKG makeself-release-2.5.0-patch1.tar.gz)
15+ 
16+if(POLICY CMP0135)
17+ cmake_policy(SET CMP0135 NEW)
18+endif()
19+ 
20+# 默认配置的makeself还是不存在则下载
21+if (NOT EXISTS "${MAKESELF_PATH}/makeself-header.sh" OR NOT EXISTS "${MAKESELF_PATH}/makeself.sh")
22+ # 优先查找本地压缩包
23+ if(EXISTS "${CANN_3RD_LIB_PATH}/pkg/${MAKESELF_VERSION_PKG}")
24+ set(REQ_URL "file://${CANN_3RD_LIB_PATH}/pkg/${MAKESELF_VERSION_PKG}")
25+ message(STATUS "[ThirdPartyLib][makeself] found in ${REQ_URL}.")
26+ elseif(EXISTS "${CANN_3RD_LIB_PATH}/${MAKESELF_VERSION_PKG}")
27+ set(REQ_URL "file://${CANN_3RD_LIB_PATH}/${MAKESELF_VERSION_PKG}")
28+ message(STATUS "[ThirdPartyLib][makeself] found in ${REQ_URL}.")
29+ else()
30+ set(REQ_URL "https://gitcode.com/cann-src-third-party/makeself/releases/download/release-2.5.0-patch1.0/${MAKESELF_VERSION_PKG}")
31+ message(STATUS "[ThirdPartyLib][makeself] ${REQ_URL} not found, need download.")
32+ endif()
33+ 
34+ include(FetchContent)
35+ FetchContent_Declare(
36+ ${MAKESELF_NAME}
37+ URL ${REQ_URL}
38+ URL_HASH SHA256=bfa730a5763cdb267904a130e02b2e48e464986909c0733ff1c96495f620369a
39+ DOWNLOAD_DIR ${CANN_3RD_LIB_PATH}/pkg
40+ SOURCE_DIR "${MAKESELF_PATH}" # 直接解压到此目录
41+ )
42+ FetchContent_MakeAvailable(${MAKESELF_NAME})
43+ execute_process(
44+ COMMAND chmod 700 "${CMAKE_BINARY_DIR}/makeself/makeself.sh"
45+ COMMAND chmod 700 "${CMAKE_BINARY_DIR}/makeself/makeself-header.sh"
46+ -E env
47+ CMAKE_TLS_VERIFY=0
48+ RESULT_VARIABLE CHMOD_RESULT
49+ ERROR_VARIABLE CHMOD_ERROR
50+ )
51+endif()
52+ 
53+# 验证文件存在
54+if(NOT EXISTS "${MAKESELF_PATH}/makeself.sh")
55+ message(FATAL_ERROR "makeself.sh not found at ${MAKESELF_PATH}")
56+endif()
57+ 
58+message(STATUS "MAKESELF_PATH: ${MAKESELF_PATH}")
Acmake/variables.cmake+49-0
@@ -0,0 +1,49 @@
1+# ----------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# ----------------------------------------------------------------------------
10+# ops-fft 安装路径定义
11+ 
12+# 包名称
13+set(PKG_NAME "ops_fft")
14+ 
15+# 默认 KERNEL_ARCH(简化处理 ascend950)
16+set(KERNEL_ARCH "ascend950")
17+ 
18+# 架构检测
19+if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
20+ set(ARCH x86_64)
21+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|arm")
22+ set(ARCH aarch64)
23+else()
24+ message(WARNING "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
25+ set(ARCH x86_64)
26+endif()
27+ 
28+# Built-in 算子包安装路径 (简化版: lib64 和 include)
29+set(PATH_NAME "ops_fft")
30+ 
31+# 头文件安装到 ops_fft/include/
32+set(OPS_FFT_INC_INSTALL_DIR ops_fft/include)
33+# 库文件安装到 ops_fft/lib64/
34+set(OPS_FFT_LIB_INSTALL_DIR ops_fft/lib64)
35+# 版本信息
36+set(VERSION_INFO_INSTALL_DIR ops_fft)
37+ 
38+# 输出路径
39+set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/build_out)
40+ 
41+# 打包相关路径
42+set(SCRIPT_PACKAGE_DIR ${CMAKE_SOURCE_DIR}/scripts/package)
43+set(COMMON_SCRIPT_DIR ${SCRIPT_PACKAGE_DIR}/common)
44+set(OPS_FFT_SCRIPT_DIR ${SCRIPT_PACKAGE_DIR}/ops_fft/scripts)
45+ 
46+message(STATUS "Package: ${PKG_NAME}")
47+message(STATUS "Architecture: ${ARCH}")
48+message(STATUS "Kernel Arch: ${KERNEL_ARCH}")
49+message(STATUS "Install Prefix: ${CMAKE_INSTALL_PREFIX}")
Adocs/README.md+42-0
@@ -0,0 +1,42 @@
1+# 项目文档
2+ 
3+## 目录说明
4+ 
5+关键目录结构如下:
6+ 
7+```
8+docs/
9+├── zh/ # 中文文档目录
10+│ ├── context/ # 公共文档目录
11+│ │ ├── build.md # build.sh 参数说明
12+│ │ ├── dir_structure.md # 项目目录结构
13+│ │ └── quick_install.md # 环境部署指南
14+│ ├── debug/ # 调试调优文档目录
15+│ │ └── op_debug_prof.md # 算子调试调优
16+│ ├── develop/ # 开发文档目录
17+│ │ ├── operator_development_guide.md # 算子开发指南
18+│ │ └── test_writing_guide.md # 测试编写指南
19+│ ├── invocation/ # 算子调用文档目录
20+│ │ └── quick_op_invocation.md # 算子调用指南
21+│ ├── op_list.md # 算子列表
22+│ └── README.md # 中文文档索引
23+└── README.md # 文档总入口
24+```
25+ 
26+## 文档列表
27+ 
28+| 文档 | 说明 |
29+| :--- | :--- |
30+| [算子列表](zh/op_list.md) | 介绍项目包含的所有算子清单 |
31+| [环境部署](zh/context/quick_install.md) | 介绍项目的基础环境搭建 |
32+| [build 参数说明](zh/context/build.md) | 介绍 build.sh 脚本功能和参数含义 |
33+| [算子开发指南](zh/develop/operator_development_guide.md) | 介绍如何从零开发一个新算子 |
34+| [测试编写指南](zh/develop/test_writing_guide.md) | 介绍如何快速编写算子测试文件 |
35+| [算子调用指南](zh/invocation/quick_op_invocation.md) | 介绍算子编译、安装和调用方法 |
36+| [算子调试调优](zh/debug/op_debug_prof.md) | 介绍常见的算子调试、调优方法 |
37+ 
38+## 附录
39+ 
40+| 文档 | 说明 |
41+| :--- | :--- |
42+| [项目目录](zh/context/dir_structure.md) | 介绍项目完整的目录结构和各目录/文件的作用 |
Adocs/zh/context/build.md+169-0
@@ -0,0 +1,169 @@
1+# build.sh 参数说明
2+ 
3+## 简介
4+ 
5+build.sh 是 ops-fft 项目的构建脚本,位于项目根目录下。该脚本通过配置不同参数实现多种功能,包括编译算子、运行测试、生成安装包等。
6+ 
7+## 使用方法
8+ 
9+### 1. 配置环境变量
10+ 
11+在使用 build.sh 之前,需要先配置 CANN 环境变量:
12+ 
13+```bash
14+# 默认路径安装
15+source /usr/local/Ascend/cann/set_env.sh
16+ 
17+# 验证环境变量
18+echo $ASCEND_HOME_PATH
19+```
20+ 
21+### 2. 构建命令格式
22+ 
23+```bash
24+./build.sh [OPTIONS]
25+```
26+ 
27+## 参数说明
28+ 
29+build.sh 支持多种功能,可通过 `--help` 参数查看所有选项:
30+ 
31+```bash
32+./build.sh --help
33+```
34+ 
35+| 参数 | 必选/可选 | 说明 |
36+|------|----------|------|
37+| `--ops=OP_LIST` | 可选 | 指定要编译的算子列表,多个算子用逗号分隔(如:`--ops=rfft1_d,irfft1_d`)。不指定时编译所有算子。 |
38+| `--run` | 可选 | 编译后执行测试。需要配合 `BUILD_TESTING=ON` 使用。 |
39+| `--pkg` | 可选 | 编译并打包成 .run 安装包。 |
40+| `--soc=SOC` | 可选 | 指定目标 SoC 型号,支持大小写不敏感输入(如:`--soc=ascend950``--soc=Ascend950`)。默认为 `Ascend950`。 |
41+| `-j[N]` | 可选 | 指定编译线程数,默认为 8(如:`-j16`)。若线程数超过 CPU 核心数,会自动调整为 CPU 核心数。 |
42+| `--test-timeout=N` | 可选 | 指定测试超时时间(单位:秒),默认为 300。仅在 `--run` 模式下有效。 |
43+| `-h, --help` | 可选 | 显示帮助信息。 |
44+ 
45+## 支持的 SoC 型号
46+ 
47+| SoC 型号 | SOC_VERSION(CANN 编译器) | 说明 |
48+|---------|---------------------------|------|
49+| Ascend950 | ascend950dt_9595 | 默认支持(dav-3510) |
50+| Ascend910B | ascend910b3 | dav-2201 |
51+| Ascend910_93 | ascend910_93 | dav-2201 |
52+| Ascend910 | ascend910 | dav-2101 |
53+| Ascend310P | ascend310p | dav-2101 |
54+| Ascend310B | ascend310b | dav-2101 |
55+ 
56+## 使用示例
57+ 
58+### 基本编译
59+ 
60+```bash
61+# 编译所有算子(默认 8 线程)
62+./build.sh
63+ 
64+# 编译指定算子
65+./build.sh --ops=rfft1_d
66+ 
67+# 编译多个算子
68+./build.sh --ops=rfft1_d,irfft1_d
69+ 
70+# 使用 16 线程编译
71+./build.sh -j16
72+```
73+ 
74+### 编译并测试
75+ 
76+```bash
77+# 编译所有算子并运行测试
78+./build.sh --run
79+ 
80+# 编译指定算子并运行测试
81+./build.sh --ops=rfft1_d --run
82+ 
83+# 指定测试超时时间(600 秒)
84+./build.sh --run --test-timeout=600
85+```
86+ 
87+### 打包
88+ 
89+```bash
90+# 编译所有算子并打包(默认 SoC: Ascend950)
91+./build.sh --pkg
92+ 
93+# 编译指定算子并打包
94+./build.sh --ops=rfft1_d --pkg
95+ 
96+# 为指定 SoC 打包
97+./build.sh --soc=Ascend910B --pkg
98+ 
99+# 大小写不敏感
100+./build.sh --soc=ascend910b --pkg
101+```
102+ 
103+### 组合使用
104+ 
105+```bash
106+# 编译 rfft1_d 算子、运行测试、使用 16 线程
107+./build.sh --ops=rfft1_d --run -j16
108+ 
109+# 编译所有算子、打包、指定 SoC
110+./build.sh --pkg --soc=Ascend910B -j16
111+```
112+ 
113+## 行为说明
114+ 
115+| 命令 | 行为 |
116+|------|------|
117+| 无参数 | 编译所有算子,不执行测试 |
118+| `--ops=rfft1_d` | 只编译 rfft1_d 算子,不执行测试 |
119+| `--ops=rfft1_d,irfft1_d` | 编译 rfft1_d 和 irfft1_d 算子,不执行测试 |
120+| `--run` | 编译所有算子,并执行所有算子的测试 |
121+| `--ops=rfft1_d --run` | 编译 rfft1_d 算子,并执行 rfft1_d 算子的测试 |
122+| `--ops=rfft1_d,irfft1_d --run` | 编译 rfft1_d、irfft1_d 算子,并执行这些算子的测试 |
123+| `--pkg` | 编译所有算子并打包成 .run 文件(默认 SoC: Ascend950) |
124+| `--ops=rfft1_d --pkg` | 编译 rfft1_d 算子并打包成 .run 文件 |
125+| `--soc=ascend950 --pkg` | 为 Ascend950 芯片打包(支持小写) |
126+ 
127+## 输出说明
128+ 
129+### 编译输出
130+ 
131+编译成功后,生成的文件位于 `build/` 目录:
132+ 
133+```
134+build/
135+├── libops_fft.so # 动态库
136+├── tests/
137+│ └── all_ops_test # 测试可执行文件
138+└── ...
139+```
140+ 
141+### 打包输出
142+ 
143+打包成功后,会在 `build/` 目录生成 .run 文件:
144+ 
145+```
146+build_out/cann-{soc}-ops-fft_{version}_linux-{arch}.run
147+```
148+ 
149+例如:
150+- `cann-950-ops-fft_9.0.0_linux-x86_64.run`
151+- `cann-910b-ops-fft_9.0.0_linux-aarch64.run`
152+ 
153+## 注意事项
154+ 
155+1. **环境变量要求**:必须设置 `ASCEND_HOME_PATH` 环境变量,否则脚本会报错退出。
156+ 
157+2. **线程数限制**:如果指定的线程数超过 CPU 核心数,脚本会自动调整为 CPU 核心数。
158+ 
159+3. **测试超时**:默认测试超时时间为 300 秒,可根据实际情况调整。
160+ 
161+4. **SoC 大小写**`--soc` 参数支持大小写不敏感输入,脚本会自动标准化为首字母大写、其余小写的格式。
162+ 
163+5. **算子验证**:如果使用 `--ops` 指定了不存在的算子,脚本会列出所有可用的算子并报错退出。
164+ 
165+## 相关文档
166+ 
167+- [环境部署](quick_install.md)
168+- [算子调用](../invocation/quick_op_invocation.md)
169+- [算子开发](../develop/aicore_develop_guide.md)
Adocs/zh/context/dir_structure.md+103-0
@@ -0,0 +1,103 @@
1+# 项目目录
2+ 
3+> 本章罗列的部分目录是可选的,请以实际交付件为准。尤其**单算子目录**,不同场景下交付件有差异。
4+ 
5+项目全量目录层级介绍如下:
6+ 
7+```
8+├── cmake # 项目工程编译目录
9+│ ├── func.cmake # 公共函数
10+│ ├── init_env.cmake # 环境初始化
11+│ ├── package.cmake # 打包配置
12+│ ├── run_package.cmake # 打包脚本
13+│ └── third_party # 第三方依赖配置
14+│ └── opbase.cmake # opbase依赖配置
15+├── docs # 项目相关文档目录
16+│ ├── README.md # 文档目录索引
17+│ └── zh # 中文文档目录
18+│ ├── op_list.md # 算子列表
19+│ ├── context # 公共文档目录
20+│ ├── invocation # 算子调用文档目录
21+│ ├── develop # 算子开发文档目录
22+│ └── debug # 调试调优文档目录
23+├── include # 头文件目录
24+│ └── cann_ops_fft.h # API头文件
25+├── scripts # 脚本目录,包含自定义算子、Kernel构建相关配置文件
26+│ └── package # 打包相关脚本
27+│ └── ops_fft/
28+│ ├── ops_fft.xml # 打包配置文件
29+│ └── scripts/ # 安装/卸载脚本
30+├── src # 源码目录
31+│ ├── CMakeLists.txt # 算子编译入口
32+# ├── rfft1_d # rfft1_d算子目录
33+│ │ ├── CMakeLists.txt # 算子编译配置文件
34+│ │ ├── rfft1_d_kernel.cpp # Kernel实现文件
35+│ │ ├── rfft1_d_host.cpp # Host侧代码
36+│ │ ├── arch35 # Ascend950特有算子代码
37+│ │ │ └── rfft1_d_struct.h # 算子结构定义
38+│ │ └── tests # 算子测试用例目录
39+│ │ ├── rfft1_d_test.cpp
40+│ │ └── rfft1_d_test.h
41+│ └── common # 算子公共代码
42+│ └── tests
43+├── tests # 项目级测试目录
44+├── third_party # 第三方依赖目录
45+│ ├── makeself # makeself打包工具
46+│ └── pkg # 预打包的依赖文件
47+├── CMakeLists.txt # 项目工程cmakelist入口
48+├── CONTRIBUTING.md # 项目贡献指南文件
49+├── README.md # 项目工程总介绍文档
50+├── QUICKSTART.md # 快速入门指南
51+├── SECURITY.md # 项目安全声明文件
52+├── build.sh # 项目工程编译脚本
53+└── version.info # 项目版本信息
54+```
55+ 
56+## 目录说明
57+ 
58+### 核心目录
59+ 
60+| 目录/文件 | 说明 |
61+| :--- | :--- |
62+| `src/` | 算子源码目录,包含所有算子的实现代码 |
63+| `src/rfft1_d/` | rfft1_d算子目录,实现一维实数FFT运算 |
64+| `src/common/` | 算子公共代码,包含通用工具函数和数据结构 |
65+| `include/` | API头文件目录 |
66+| `cmake/` | CMake编译配置文件 |
67+ 
68+### 文档目录
69+ 
70+| 目录/文件 | 说明 |
71+| :--- | :--- |
72+| `docs/` | 项目文档目录 |
73+| `docs/zh/` | 中文文档目录 |
74+| `docs/zh/context/` | 公共文档,如环境部署、目录介绍等 |
75+| `docs/zh/invocation/` | 算子调用相关文档 |
76+| `docs/zh/develop/` | 算子开发相关文档 |
77+| `docs/zh/debug/` | 调试调优相关文档 |
78+ 
79+### 构建相关
80+ 
81+| 文件 | 说明 |
82+| :--- | :--- |
83+| `build.sh` | 项目编译脚本,支持多种编译选项 |
84+| `CMakeLists.txt` | CMake配置文件 |
85+| `version.info` | 版本信息文件 |
86+ 
87+## 算子目录结构
88+ 
89+每个算子目录(如`src/rfft1_d/`)的典型结构如下:
90+ 
91+```
92+${op_name}/ # 算子名的小写下划线形式
93+├── CMakeLists.txt # 算子编译配置文件
94+├── ${op_name}_kernel.cpp # Kernel实现文件
95+├── ${op_name}_host.cpp # Host侧代码
96+├── arch35/ # Ascend950特有实现
97+│ └── ${op_name}_struct.h # 算子结构定义
98+└── tests/ # 测试用例目录
99+ ├── ${op_name}_test.cpp # 算子测试用例
100+ └── ${op_name}_test.h # 测试头文件
101+```
102+ 
103+> **说明**:不同算子的交付件可能有差异,请以实际目录为准。
Adocs/zh/context/quick_install.md+146-0
@@ -0,0 +1,146 @@
1+# 环境部署
2+ 
3+基于本项目进行[算子调用](../invocation/quick_op_invocation.md)或[算子开发](../develop/aicore_develop_guide.md)之前,需要参考下述步骤完成基础环境搭建。
4+ 
5+## 前提条件
6+ 
7+使用本项目前,请确保如下基础依赖、NPU驱动和固件已安装。
8+ 
9+1. **安装依赖**
10+ 
11+ 本项目源码编译用到的依赖如下,请注意版本要求。
12+ 
13+ - python >= 3.7.0, < 3.10
14+ - gcc >= 7.3.0
15+ - cmake >= 3.16.0
16+ - pigz(可选,安装后可提升打包速度,建议版本 >= 2.4)
17+ - dos2unix
18+ - gawk
19+ - make
20+ 
21+ 上述依赖包可通过项目根目录install\_deps.sh安装,命令如下,若遇到不支持系统,请参考该文件自行适配。
22+ ```bash
23+ bash install_deps.sh
24+ ```
25+ 
26+2. **安装驱动与固件(运行态依赖)**
27+ 
28+ 运行算子时必须安装驱动与固件,若仅编译算子,可跳过本操作。
29+ 
30+ 单击[下载链接](https://www.hiascend.com/hardware/firmware-drivers/community),根据实际产品型号和环境架构,获取对应的`Ascend-hdk-<chip_type>-npu-driver_<version>_linux-<arch>.run``Ascend-hdk-<chip_type>-npu-firmware_<version>_linux-<arch>.run`包。
31+ 
32+ 安装指导详见《[CANN 软件安装指南](https://www.hiascend.com/document/redirect/CannCommunityInstSoftware)》。
33+ 
34+## 环境准备(二选一)
35+ 
36+算子开发的第一步是准备一个包含CANN及算子仓前置依赖的环境,使用Docker镜像是最高效的方式。
37+ 
38+### 使用Docker部署
39+ 
40+ > **说明:**
41+ > - 该部署方式适用于Ascend 950系列产品。
42+ > - 镜像文件比较大,下载需要一定时间,请您耐心等待。
43+ 
44+#### 1. 下载镜像
45+ 
46+ 1. 以root用户登录宿主机。确保宿主机已安装Docker引擎(版本1.11.2及以上)。
47+ 2. 从昇腾镜像仓库拉取已预集成CANN软件包及`ops-fft`所需依赖的镜像。命令如下,根据实际架构选择:
48+ ```bash
49+ # 示例:拉取ARM架构的CANN开发镜像
50+ docker pull --platform=arm64 swr.cn-south-1.myhuaweicloud.com/ascendhub/cann:9.0.0-950-ubuntu22.04-py3.10-ops
51+ # 示例:拉取X86架构的CANN开发镜像
52+ docker pull --platform=amd64 swr.cn-south-1.myhuaweicloud.com/ascendhub/cann:9.0.0-950-ubuntu22.04-py3.10-ops
53+ ```
54+ 
55+#### 2. 运行Docker
56+ 
57+拉取镜像后,需要以特定参数启动容器,以便容器内能访问宿主的昇腾设备。
58+ 
59+```bash
60+docker run --name cann_container --device /dev/davinci0 --device /dev/davinci_manager --device /dev/devmm_svm --device /dev/hisi_hdc -v /usr/local/dcmi:/usr/local/dcmi -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ -v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info -v /etc/ascend_install.info:/etc/ascend_install.info -it swr.cn-south-1.myhuaweicloud.com/ascendhub/cann:9.0.0-950-ubuntu22.04-py3.10-ops bash
61+```
62+ 
63+| 参数 | 说明 | 注意事项 |
64+| :--- | :--- | :--- |
65+| `--name cann_container` | 为容器指定名称,便于管理。 | 可自定义。 |
66+| `--device /dev/davinci0` | 核心:将宿主机的NPU设备卡映射到容器内,可指定映射多张NPU设备卡。 | 必须根据实际情况调整:`davinci0`对应系统中的第0张NPU卡。请先在宿主机执行 `npu-smi info`命令,根据输出显示的设备号(如`NPU 0`, `NPU 1`)来修改此编号。|
67+| `--device /dev/davinci_manager` | 映射NPU设备管理接口。 | |
68+| `--device /dev/devmm_svm` | 映射设备内存管理接口。 | |
69+| `--device /dev/hisi_hdc` | 映射主机与设备间的通信接口。 | |
70+| `-v /usr/local/dcmi:/usr/local/dcmi` | 挂载设备容器管理接口(DCMI)相关工具和库。 | |
71+| `-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi` | 挂载`npu-smi`工具。 | 使容器内可以直接运行此命令来查询NPU状态和性能信息。|
72+| `-v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/` | 关键挂载:将宿主机的NPU驱动库映射到容器内。 | |
73+| `-v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info` | 挂载驱动版本信息文件。 | |
74+| `-v /etc/ascend_install.info:/etc/ascend_install.info` | 挂载CANN软件安装信息文件。 | |
75+| `-it` | `-i`(交互式)和 `-t`(分配伪终端)的组合参数。 | |
76+| 镜像名称 | 指定要运行的Docker镜像。 |请确保此镜像名和标签(tag)与你通过`docker pull`拉取的镜像完全一致。 |
77+| `bash` | 容器启动后立即执行的命令。 | |
78+ 
79+ 
80+### 手动安装CANN包
81+ 
82+1. **安装社区CANN toolkit包**
83+ 
84+ 根据实际产品型号和环境架构,获取对应的`Ascend-cann-toolkit_${cann_version}_linux-${arch}.run`包。
85+ 
86+ Ascend 950系列产品:单击[下载链接](https://mirror-centralrepo.devcloud.cn-north-4.huaweicloud.com/artifactory/cann-run-release/software/9.0.0-alpha.1/)获取软件包。
87+ 
88+ ```bash
89+ # 确保安装包具有可执行权限
90+ chmod +x Ascend-cann-toolkit_${cann_version}_linux-${arch}.run
91+ # 安装命令
92+ ./Ascend-cann-toolkit_${cann_version}_linux-${arch}.run --install --force --install-path=${install_path}
93+ ```
94+ - $\{cann\_version\}:表示CANN包版本号。
95+ - $\{arch\}:表示CPU架构,如aarch64、x86_64。
96+ - $\{install\_path\}:表示指定安装路径,默认安装在`/usr/local/Ascend`目录。
97+ 
98+2. **安装社区版CANN ops包(运行态依赖)**
99+ 
100+ 运行算子时必须安装本包,若仅编译算子,可跳过本操作。
101+ 
102+ 根据实际产品型号和环境架构,获取对应的`Ascend-cann-${soc_name}-ops_${cann_version}_linux-${arch}.run`包。
103+ 
104+ Ascend 950系列产品:单击[下载链接](https://mirror-centralrepo.devcloud.cn-north-4.huaweicloud.com/artifactory/cann-run-release/software/9.0.0-alpha.1/)获取软件包。
105+ 
106+ ```bash
107+ # 确保安装包具有可执行权限
108+ chmod +x Ascend-cann-${soc_name}-ops_${cann_version}_linux-${arch}.run
109+ # 安装命令
110+ ./Ascend-cann-${soc_name}-ops_${cann_version}_linux-${arch}.run --install --install-path=${install_path}
111+ ```
112+ - $\{soc\_name\}:表示NPU型号名称,即$\{soc\_version\}删除"ascend"后剩余的内容,如`950`
113+ - $\{install\_path\}:表示指定安装路径,需要与toolkit包安装在相同路径,默认安装在`/usr/local/Ascend`目录。
114+ 
115+## 环境验证
116+ 
117+安装CANN包或进入容器后,验证环境和驱动是否正常。
118+ 
119+* **检查NPU设备**
120+ ```bash
121+ # 运行npu-smi,若能正常显示设备信息,则驱动正常
122+ npu-smi info
123+ ```
124+* **检查CANN安装**
125+ ```bash
126+ # 查看CANN Toolkit版本信息
127+ cat /usr/local/Ascend/ascend-toolkit/latest/opp/version.info
128+ ```
129+ 
130+## 环境变量配置
131+ 
132+根据实际场景,选择合适的命令。
133+ 
134+```bash
135+# 默认路径安装,以root用户为例(非root用户,将/usr/local替换为${HOME})
136+source /usr/local/Ascend/cann/set_env.sh
137+# 指定路径安装
138+# source ${install_path}/cann/set_env.sh
139+```
140+ 
141+## 源码下载
142+ 
143+```bash
144+# 下载项目源码,以master分支为例
145+git clone https://gitcode.com/cann/ops-fft.git
146+```
Adocs/zh/debug/op_debug_prof.md+92-0
@@ -0,0 +1,92 @@
1+# 算子调试调优
2+ 
3+本文档介绍ops-fft项目中常见的算子调试和调优方法。
4+ 
5+## 概述
6+ 
7+在算子开发过程中,可能会遇到以下问题:
8+ 
9+- 算子功能异常,输出结果不正确
10+- 算子性能不达标,需要优化
11+- 算子运行时报错
12+ 
13+针对这些问题,本文档提供以下调试和调优方法:
14+ 
15+- [日志调试](#日志调试):通过日志定位问题
16+- [性能调优](#性能调优):优化算子性能
17+ 
18+## 日志调试
19+ 
20+### 编译时日志
21+ 
22+使用`-v`参数查看详细编译输出:
23+ 
24+```bash
25+bash build.sh -v --run
26+```
27+ 
28+### 运行时日志
29+ 
30+CANN提供了多种日志级别,可通过环境变量配置:
31+ 
32+```bash
33+# 设置日志级别
34+export ASCEND_GLOBAL_LOG_LEVEL=3 # 0-debug, 1-info, 2-warning, 3-error
35+ 
36+# 设置日志路径
37+export ASCEND_GLOBAL_EVENT_ENABLE=0
38+```
39+ 
40+### 常见错误排查
41+ 
42+| 错误信息 | 可能原因 | 解决方案 |
43+| :--- | :--- | :--- |
44+| `Out of memory` | 内存不足 | 减小tile size或增加workspace |
45+| `Invalid tiling parameters` | Tiling参数错误 | 检查TilingData结构体定义 |
46+| `Kernel launch failed` | Kernel启动失败 | 检查核函数定义和参数 |
47+ 
48+## 性能调优
49+ 
50+### 1. Tiling优化
51+ 
52+合理设置Tiling参数可以提高算子性能:
53+ 
54+- **Block切分**:根据核数均匀分配数据
55+- **UB切分**:充分利用Unified Buffer空间
56+- **对齐要求**:确保数据地址和大小满足硬件对齐要求
57+ 
58+### 2. 内存优化
59+ 
60+- 减少内存拷贝次数
61+- 使用双缓冲(Double Buffer)技术
62+- 合理规划workspace大小
63+ 
64+### 3. 计算优化
65+ 
66+- 向量化计算:使用Ascend C向量指令
67+- 流水并行:合理使用多队列
68+- 指令融合:减少中间结果存储
69+ 
70+## 调试工具
71+ 
72+### msProf性能分析
73+ 
74+使用msProf工具进行性能分析:
75+ 
76+```bash
77+msprof --output=./prof_out ./your_program
78+```
79+ 
80+### DumpTensor数据导出
81+ 
82+导出算子中间结果进行调试:
83+ 
84+```bash
85+export ASCEND_WORK_PATH=./dump_out
86+export ASCEND_GLOBAL_LOG_LEVEL=0
87+```
88+ 
89+## 更多帮助
90+ 
91+- [CANN 开发文档](https://www.hiascend.com/document)
92+- [Ascend C 性能优化指南](https://hiascend.com/document/redirect/CannCommunityAscendCPerf)
Adocs/zh/develop/operator_development_guide.md+298-0
@@ -0,0 +1,298 @@
1+# 算子开发指南
2+ 
3+## 目录结构
4+ 
5+开发一个算子需要以下文件:
6+ 
7+```
8+src/
9+├── rfft1_d/ # 示例:Rfft1_d 算子
10+│ ├── rfft1_d.cpp # Host + Kernel 实现
11+│ ├── rfft1_d_struct.h # Tiling 数据结构(可选)
12+│ ├── CMakeLists.txt # 编译配置
13+│ └── tests/ # 测试目录(强烈推荐)
14+│ ├── rfft1_d_test.h
15+│ └── rfft1_d_test.cpp
16+├── ... # 其他算子
17+└── CMakeLists.txt
18+```
19+ 
20+**说明**
21+- Host 和 Kernel 可以合并为一个 `.cpp` 文件
22+- TilingData 可以定义在 `.cpp` 文件中,也可以独立为 `_struct.h`
23+- `arch35/` 目录仅在需要区分不同 SOC 架构时使用
24+- 测试文件强烈推荐,但不是必需的
25+ 
26+---
27+ 
28+## 文件说明
29+ 
30+### 1. Host + Kernel 实现
31+ 
32+**文件**`<op_name>.cpp`
33+ 
34+**作用**
35+- **Host 部分**:实现对外接口、Tiling 计算、内存管理、核函数调用
36+- **Kernel 部分**:实现实际的核函数逻辑
37+ 
38+**基本结构**
39+ 
40+```cpp
41+#include "acl/acl.h"
42+#include "kernel_operator.h"
43+ 
44+#define GM_ADDR uint8_t*
45+ 
46+// ========== Tiling 数据结构 ==========
47+namespace <OpName>Op {
48+ struct <OpName>TilingData {
49+ int64_t totalLength;
50+ int64_t usedCoreNum;
51+ // ... 其他 Tiling 参数
52+ };
53+}
54+ 
55+// ========== Host 部分:对外接口 ==========
56+ 
57+extern "C" aclError acltensor<OpName>(float* input, float* output,
58+ int64_t size, void* stream)
59+{
60+ // 1. 参数检查
61+ if (input == nullptr || output == nullptr || size <= 0) {
62+ return ACL_ERROR_INVALID_PARAM;
63+ }
64+ 
65+ // 2. 计算 Tiling 数据(可用函数封装)
66+ <OpName>Op::<OpName>TilingData tilingData;
67+ tilingData.totalLength = size;
68+ tilingData.usedCoreNum = CalculateCoreNum(size); // 自定义函数
69+ // ... 其他 Tiling 参数
70+ 
71+ // 3. 分配设备内存
72+ uint8_t *inputDevice, *outputDevice, *tilingDevice;
73+ aclrtMalloc((void**)&inputDevice, size * sizeof(float), ACL_MEM_MALLOC_HUGE_FIRST);
74+ aclrtMalloc((void**)&outputDevice, size * sizeof(float), ACL_MEM_MALLOC_HUGE_FIRST);
75+ aclrtMalloc((void**)&tilingDevice, sizeof(tilingData), ACL_MEM_MALLOC_HUGE_FIRST);
76+ 
77+ // 4. 拷贝数据到设备(先转换为 uint8_t*)
78+ uint8_t* inputHost = reinterpret_cast<uint8_t*>(input);
79+ uint8_t* outputHost = reinterpret_cast<uint8_t*>(output);
80+ aclrtMemcpy(inputDevice, size * sizeof(float), inputHost, size * sizeof(float), ACL_MEMCPY_HOST_TO_DEVICE);
81+ aclrtMemcpy(tilingDevice, sizeof(tilingData), &tilingData, sizeof(tilingData), ACL_MEMCPY_HOST_TO_DEVICE);
82+ 
83+ // 5. 调用核函数 <<<Block, workspace, stream>>>
84+ <op_name>_kernel_do(inputDevice, outputDevice, tilingDevice,
85+ nullptr, tilingData.usedCoreNum, stream);
86+ 
87+ // 6. 同步并拷贝结果回主机
88+ aclrtSynchronizeStream(stream);
89+ aclrtMemcpy(outputHost, size * sizeof(float), outputDevice, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST);
90+ 
91+ // 7. 释放设备内存
92+ aclrtFree(inputDevice);
93+ aclrtFree(outputDevice);
94+ aclrtFree(tilingDevice);
95+ 
96+ return ACL_SUCCESS;
97+}
98+ 
99+// ========== Kernel 部分:核函数实现 ==========
100+ 
101+using namespace AscendC;
102+ 
103+extern "C" __global__ __aicore__ void <op_name>(GM_ADDR input, GM_ADDR output,
104+ GM_ADDR tiling)
105+{
106+ // Kernel 类型声明
107+ KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
108+ 
109+ // 初始化
110+ TPipe pipe;
111+ // ... 初始化 LocalTensor、GlobalTensor、TQue 等
112+ 
113+ // 解析 Tiling 数据
114+ auto tilingData = (<OpName>Op::<OpName>TilingData*)tiling;
115+ 
116+ // 核心计算逻辑
117+ for (int i = 0; i < tilingData->blockLoopCnt; ++i) {
118+ // 1. DataCopy: GM -> LocalTensor
119+ // 2. 计算
120+ // 3. DataCopy: LocalTensor -> GM
121+ }
122+}
123+ 
124+// 核函数封装
125+void <op_name>_kernel_do(GM_ADDR input, GM_ADDR output, GM_ADDR tiling,
126+ GM_ADDR workspace, uint32_t numBlocks, void *stream)
127+{
128+ <op_name><<<numBlocks, workspace, stream>>>(input, output, tiling);
129+}
130+```
131+ 
132+**Host 部分关键点**
133+1. 定义 TilingData 结构体(或 include 独立的 `_struct.h`
134+2. 计算 Tiling 参数
135+3. 实现对外接口(如 `acltensorRfft1_d``acltensorIfft1_d` 等)
136+4. 使用 `<<<numBlocks, workspace, stream>>>` 调用核函数
137+ 
138+**Kernel 部分关键点**
139+1. 使用 `__global__ __aicore__` 标记核函数
140+2. 实现 GM ↔ LocalTensor 的数据搬运
141+3. 实现核心计算逻辑
142+ 
143+---
144+ 
145+### 2. Tiling 数据结构(可选)
146+ 
147+**文件**`<op_name>_struct.h`
148+ 
149+**作用**:定义 Host 传递给 Kernel 的 Tiling 参数
150+ 
151+**基本结构**
152+ 
153+```cpp
154+#ifndef <OP_NAME>_STRUCT_H
155+#define <OP_NAME>_STRUCT_H
156+ 
157+#include <cstdint>
158+ 
159+namespace <OpName>Op {
160+ 
161+struct <OpName>TilingData {
162+ int64_t totalLength;
163+ int64_t usedCoreNum;
164+ int64_t blockFormer;
165+ int64_t blockLoopCnt;
166+ int64_t blockTail;
167+ // ... 其他 Tiling 参数
168+};
169+ 
170+} // namespace <OpName>Op
171+ 
172+#endif
173+```
174+ 
175+**说明**
176+- 这是一个简单的 C 结构体
177+- 只包含基本数据类型(int64_t 等)
178+- Host 计算参数,Kernel 读取参数
179+- 也可以直接定义在 `.cpp` 文件中
180+ 
181+---
182+ 
183+### 3. 编译配置
184+ 
185+**文件**`CMakeLists.txt`
186+ 
187+```cmake
188+register_operator(
189+ NAME <op_name>
190+ ARCH_DIR arch35 # 如果需要区分架构才加这个参数
191+)
192+```
193+ 
194+---
195+ 
196+### 4. 测试文件(强烈推荐)
197+ 
198+参见 [测试编写指南](test_writing_guide.md)。
199+ 
200+**说明**:虽然测试文件不是必需的,但强烈建议为每个算子编写单元测试,以确保算子实现的正确性。
201+ 
202+---
203+ 
204+## 开发流程
205+ 
206+### 步骤 1:创建目录和文件
207+ 
208+```bash
209+mkdir -p src/<op_name>/tests
210+touch src/<op_name>/<op_name>.cpp
211+touch src/<op_name>/CMakeLists.txt
212+```
213+ 
214+(可选)独立的 struct 文件:
215+```bash
216+touch src/<op_name>/<op_name>_struct.h
217+```
218+ 
219+(可选)测试文件:
220+```bash
221+touch src/<op_name>/tests/<op_name>_test.h
222+touch src/<op_name>/tests/<op_name>_test.cpp
223+```
224+ 
225+### 步骤 2:编写 Host + Kernel 实现
226+ 
227+`<op_name>.cpp` 中:
228+1. 定义 TilingData 结构体(或 include 独立的 `_struct.h`
229+2. 实现对外接口(如 `acltensorRfft1_d``acltensorIfft1_d` 等)
230+3. 计算 Tiling 参数
231+4. 实现核函数(使用 `__global__ __aicore__`
232+ 
233+### 步骤 3:配置编译
234+ 
235+`CMakeLists.txt` 中注册算子。
236+ 
237+### 步骤 4:编写测试(推荐)
238+ 
239+参考 [测试编写指南](test_writing_guide.md)。
240+ 
241+### 步骤 5:编译验证
242+ 
243+```bash
244+./build.sh --ops=<op_name> --run
245+```
246+ 
247+---
248+ 
249+## 关键概念
250+ 
251+### Host vs Kernel
252+ 
253+| 层面 | 运行位置 | 职责 |
254+|------|---------|------|
255+| **Host** | CPU | 对外接口、Tiling 计算、内存管理、启动 Kernel |
256+| **Kernel** | NPU AI Core | 实际计算逻辑 |
257+ 
258+### Tiling
259+ 
260+**目的**:将大任务切分成适合 NPU 执行的小块
261+ 
262+**关键参数**
263+- `usedCoreNum` - 使用多少个 AI Core
264+- `blockFormer` - 每次迭代处理多少数据
265+- `blockLoopCnt` - 每个核迭代多少次
266+ 
267+**计算原则**
268+- 充分利用 AI Core 并行能力
269+- 数据不超过 Unified Buffer 容量
270+- 对齐到 32 字节边界
271+ 
272+### <<<>>> 核函数调用
273+ 
274+```cpp
275+<kernel_func><<<numBlocks, workspace, stream>>>(args...);
276+```
277+ 
278+**参数说明**
279+- `numBlocks` - 使用多少个 AI Core(Block)
280+- `workspace` - 共享内存指针,通常设置为 `nullptr`
281+- `stream` - ACL 执行流
282+ 
283+---
284+ 
285+## 完整示例
286+ 
287+参见 `src/rfft1_d/` 目录:
288+- `rfft1_d_host.cpp` - Host 端实现(对外接口、Tiling 计算、内存管理)
289+- `rfft1_d_kernel.cpp` - Kernel 端实现(核函数逻辑)
290+- `rfft1_d_struct.h` - Tiling 数据结构
291+- `CMakeLists.txt` - 编译配置
292+ 
293+---
294+ 
295+## 相关文档
296+ 
297+- [测试编写指南](test_writing_guide.md)
298+- [build 参数说明](../context/build.md)
Adocs/zh/develop/test_writing_guide.md+166-0
@@ -0,0 +1,166 @@
1+# 算子测试编写指南
2+ 
3+## 文件结构
4+ 
5+写一个算子的测试需要 **2 个文件**
6+ 
7+```
8+src/<op_name>/tests/
9+├── <op_name>_test.h # 头文件(必需)
10+└── <op_name>_test.cpp # 实现文件(必需)
11+```
12+ 
13+## 头文件(必需)
14+ 
15+**文件**`<op_name>_test.h`
16+ 
17+**内容**
18+```cpp
19+#pragma once
20+#include "test_common.h"
21+ 
22+namespace <OpName>Test {
23+ void run_all_tests(aclrtStream stream, OpsFftTest::TestStats& stats);
24+}
25+```
26+ 
27+**说明**
28+- 命名空间:`<OpName>Test`(首字母大写,用于隔离不同算子的测试)
29+- 函数名:`run_all_tests`(固定名称,测试框架会调用此函数)
30+- 参数说明:
31+ - `aclrtStream stream` - ACL 运行流,用于执行算子
32+ - `TestStats& stats` - 测试统计对象,用于记录测试结果
33+ 
34+---
35+ 
36+## 实现文件(必需)
37+ 
38+**文件**`<op_name>_test.cpp`
39+ 
40+**必须包含的 3 个部分**
41+ 
42+### 1. 测试用例函数(必需,至少1个)
43+ 
44+**函数签名**
45+```cpp
46+void test_xxx(aclrtStream stream, OpsFftTest::TestStats& stats);
47+```
48+ 
49+**参数说明**
50+- `stream` - 传递给算子调用,用于在 NPU 上执行
51+- `stats` - 传递给所有测试宏(TEST_ASSERT、TEST_CASE_PASS 等),用于自动统计
52+ 
53+**示例**
54+ 
55+```cpp
56+void test_xxx(aclrtStream stream, OpsFftTest::TestStats& stats) {
57+ TEST_CASE_BEGIN("test_xxx");
58+ 
59+ // 准备数据
60+ float input1[] = {1.0f, 2.0f};
61+ float input2[] = {2.0f, 3.0f};
62+ float expected[] = {3.0f, 5.0f};
63+ float output[2];
64+ 
65+ // 调用算子
66+ aclError result = acl<OpName>(input1, input2, output, 2, stream);
67+ 
68+ // 验证结果
69+ TEST_ASSERT(stats, result == ACL_SUCCESS, "failed");
70+ TEST_ASSERT_ARRAY_NEAR(stats, output, expected, 2, 1e-6f, "mismatch");
71+ 
72+ TEST_CASE_PASS(stats, "test_xxx");
73+}
74+```
75+ 
76+### 2. run_all_tests 实现(必需)
77+ 
78+```cpp
79+namespace <OpName>Test {
80+ void run_all_tests(aclrtStream stream, OpsFftTest::TestStats& stats) {
81+ test_xxx(stream, stats);
82+ // 可以添加更多测试函数
83+ }
84+}
85+```
86+ 
87+### 3. 自动注册(必需)
88+ 
89+```cpp
90+REGISTER_OP_TEST(<OpName>)
91+```
92+ 
93+---
94+ 
95+## 完整示例
96+ 
97+**my_op_test.h**
98+```cpp
99+#pragma once
100+#include "test_common.h"
101+ 
102+namespace MyOpTest {
103+ void run_all_tests(aclrtStream stream, OpsFftTest::TestStats& stats);
104+}
105+```
106+ 
107+**my_op_test.cpp**
108+```cpp
109+#include "cann_ops_fft.h"
110+#include "my_op_test.h"
111+ 
112+void test_basic(aclrtStream stream, OpsFftTest::TestStats& stats) {
113+ TEST_CASE_BEGIN("test_basic");
114+ 
115+ float x1[] = {1.0f, 2.0f};
116+ float x2[] = {2.0f, 3.0f};
117+ float expected[] = {3.0f, 5.0f};
118+ float y[2];
119+ 
120+ aclError result = aclMyOp(x1, x2, y, 2, stream);
121+ 
122+ TEST_ASSERT(stats, result == ACL_SUCCESS, "failed");
123+ TEST_ASSERT_ARRAY_NEAR(stats, y, expected, 2, 1e-6f, "mismatch");
124+ 
125+ TEST_CASE_PASS(stats, "test_basic");
126+}
127+ 
128+namespace MyOpTest {
129+ void run_all_tests(aclrtStream stream, OpsFftTest::TestStats& stats) {
130+ test_basic(stream, stats);
131+ }
132+}
133+ 
134+REGISTER_OP_TEST(MyOp)
135+```
136+ 
137+---
138+ 
139+## 可用的宏
140+ 
141+### 用例控制
142+```cpp
143+TEST_CASE_BEGIN("test_name") // 标记开始
144+TEST_CASE_PASS(stats, "test_name") // 标记通过
145+```
146+ 
147+### 断言
148+```cpp
149+TEST_ASSERT(stats, condition, "error") // 条件断言
150+TEST_ASSERT_ARRAY_EQ(stats, actual, expected, size, "error") // 精确比较
151+TEST_ASSERT_ARRAY_NEAR(stats, actual, expected, size, tol, "error") // 容差比较
152+```
153+ 
154+---
155+ 
156+## 编译运行
157+ 
158+```bash
159+./build.sh --ops=<op_name> --run
160+```
161+ 
162+---
163+ 
164+## 参考示例
165+ 
166+完整示例:`src/rfft1_d/tests/rfft1_d_test.cpp`
Adocs/zh/invocation/quick_op_invocation.md+115-0
@@ -0,0 +1,115 @@
1+# 算子调用
2+ 
3+## 前提条件
4+ 
5+- 环境部署:调用项目算子之前,请先参考[环境部署](../context/quick_install.md)完成基础环境搭建。
6+- 调用算子列表:项目可调用的算子参见[算子列表](../op_list.md)。
7+ 
8+## 编译执行
9+ 
10+基于社区版CANN包对算子源码修改时,可采用如下方式进行源码编译:
11+ 
12+- [ops-fft包](#ops-fft包):选择整个项目编译生成的包称为ops-fft包,可**完整替换**CANN包对应部分。
13+ 
14+### ops-fft包
15+ 
16+1. **编译ops-fft包**
17+ 
18+ 进入项目根目录,执行如下编译命令:
19+ 
20+ ```bash
21+ # 编译所有算子并生成安装包
22+ bash build.sh --pkg
23+ ```
24+ 
25+ 若提示如下信息,说明编译成功。
26+ 
27+ ```bash
28+ Self-extractable archive "cann-950-ops-fft_9.0.0_linux-*.run" successfully created.
29+ Build package success: build_out/cann-950-ops-fft_9.0.0_linux-*.run
30+ ```
31+ 
32+ 编译成功后,run包存放于项目根目录的build目录下。
33+ 
34+2. **安装ops-fft包**
35+ 
36+ ```bash
37+ # 安装命令
38+ ./build_out/cann-*-ops-fft-*linux*.run --full
39+ ```
40+ 
41+ ops-fft安装在`${ASCEND_HOME_PATH}/cann`路径中,`${ASCEND_HOME_PATH}`表示CANN软件安装目录。
42+ 
43+3. **配置环境变量**
44+ 
45+ ```bash
46+ source ${ASCEND_HOME_PATH}/cann/set_env.bash
47+ ```
48+ 
49+4. **(可选)卸载ops-fft包**
50+ 
51+ ```bash
52+ # 卸载命令
53+ ./${install_path}/cann/share/info/ops_fft/scripts/uninstall.sh
54+ ```
55+ 
56+## 本地验证
57+ 
58+通过项目根目录build.sh脚本,可快速调用算子和UT用例,验证项目功能是否正常,build参数介绍参见[build参数说明](../context/build.md)。
59+ 
60+### 运行测试
61+ 
62+```bash
63+# 编译并运行测试
64+bash build.sh --run
65+ 
66+# 编译指定算子并运行测试
67+bash build.sh --ops=rfft1_d --run
68+```
69+ 
70+执行测试后会打印执行结果,以rfft1_d算子为例,结果如下:
71+ 
72+```
73+all_ops_test .......... Passed * sec
74+```
75+ 
76+### 编译选项说明
77+ 
78+| 选项 | 说明 | 示例 |
79+| :--- | :--- | :--- |
80+| `--ops=NAME` | 编译指定算子 | `--ops=rfft1_d` |
81+| `--build-type=TYPE` | 构建类型(Release/Debug) | `--build-type=Debug` |
82+| `--run` | 编译后运行测试 | `--run` |
83+| `--pkg` | 生成安装包 | `--pkg` |
84+| `-j[N]` | 并行编译线程数 | `-j8` |
85+| `-v` | 详细输出 | `-v` |
86+ 
87+### 常用命令示例
88+ 
89+```bash
90+# 基本编译
91+bash build.sh
92+ 
93+# 编译并运行测试
94+bash build.sh --run
95+ 
96+# 编译指定算子并运行测试
97+bash build.sh --ops=rfft1_d --run
98+ 
99+# 编译生成安装包
100+bash build.sh --pkg
101+ 
102+# 多线程编译
103+bash build.sh -j16
104+ 
105+# 调试模式编译
106+bash build.sh --build-type=Debug --run
107+ 
108+# 详细输出
109+bash build.sh -v --run
110+```
111+ 
112+## 更多帮助
113+ 
114+- [CANN 开发文档](https://www.hiascend.com/document)
115+- [Ascend C API 参考](https://hiascend.com/document/redirect/CannCommunityAscendCApi)
Adocs/zh/op_list.md+52-0
@@ -0,0 +1,52 @@
1+# 算子列表
2+ 
3+> 说明:
4+> - **算子目录**:目录名为算子名小写下划线形式,每个目录承载该算子所有交付件,包括代码实现、文档等,目录介绍参见[项目目录](./context/dir_structure.md)。
5+> - **算子执行硬件单元**:项目中提到的算子指AI Core算子。
6+ 
7+项目提供的所有算子分类和算子列表如下:
8+ 
9+| 算子分类 | 算子目录 | op_kernel | op_host | 算子执行硬件单元 | 说明 |
10+| :--- | :--- | :---: | :---: | :--- | :--- |
11+| fft | [rfft1_d](#rfft1_d) | √ | √ | AI Core | 一维实数快速傅里叶变换,支持FP32数据类型。 |
12+ 
13+## 算子详细说明
14+ 
15+### rfft1_d
16+ 
17+对一维实数序列执行快速傅里叶变换(FFT)。
18+ 
19+**目录位置**`src/rfft1_d/`
20+ 
21+**支持的芯片**:Ascend 950
22+ 
23+**支持的数据类型**:FP32
24+ 
25+**输入**
26+- x:输入张量(实数序列)
27+ 
28+**输出**
29+- y:输出张量(FFT结果,复数序列)
30+ 
31+## 算子分类说明
32+ 
33+### fft 类算子
34+ 
35+ops-fft是[CANN](https://hiascend.com/software/cann)(Compute Architecture for Neural Networks)算子库中提供的**FFT变换算子库**,主要提供以下功能:
36+ 
37+- **rfft1_d**:一维实数FFT运算,对实数序列执行快速傅里叶变换。
38+ 
39+## 支持的芯片版本
40+ 
41+| 芯片类型 | 支持状态 |
42+| :--- | :---: |
43+| Ascend 950PR | √ |
44+| Ascend 950DT | √ |
45+ 
46+> **说明**:本项目当前主要支持 Ascend 950 系列芯片,其他芯片版本的支持情况请关注后续更新。
47+ 
48+## 快速开始
49+ 
50+如需快速体验算子调用,请参考:
51+- [环境部署](./context/quick_install.md):搭建基础环境
52+- [算子调用](./invocation/quick_op_invocation.md):编译部署并调用算子
Ainclude/cann_ops_fft.h+49-0
@@ -0,0 +1,49 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software; you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+/*!
12+ * \file cann_ops_fft.h
13+ * \brief ops-fft 公共 API 头文件 - 提供 Add 算子的对外接口
14+ */
15+ 
16+#ifndef CANN_OPS_FFT_H
17+#define CANN_OPS_FFT_H
18+ 
19+#include <stdint.h>
20+#include "acl/acl.h"
21+ 
22+/* 导出宏定义 */
23+#if defined(_WIN32) || defined(__CYGWIN__)
24+ #define ACLFFT_API __declspec(dllexport)
25+#else
26+ #define ACLFFT_API __attribute__((visibility("default")))
27+#endif
28+ 
29+#ifdef __cplusplus
30+extern "C" {
31+#endif
32+ 
33+/**
34+ * @brief Tensor Add operation: C = A + B (element-wise)
35+ *
36+ * @param x1 Input tensor A (device pointer, float type)
37+ * @param x2 Input tensor B (device pointer, float type)
38+ * @param y Output tensor (device pointer, float type)
39+ * @param n Number of elements in each tensor
40+ * @param stream ACL stream for execution
41+ * @return 0 (ACL_SUCCESS) on success, error code otherwise
42+ */
43+ACLFFT_API aclError acltensorAdd(float* x1, float* x2, float* y, int64_t n, void* stream);
44+ 
45+#ifdef __cplusplus
46+}
47+#endif
48+ 
49+#endif // CANN_OPS_FFT_H
Ascripts/check_build_dependencies.py+208-0
@@ -0,0 +1,208 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# -----------------------------------------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# -----------------------------------------------------------------------------------------------------------
12+ 
13+"""检查构建依赖。"""
14+ 
15+import argparse
16+import logging
17+import os
18+import sys
19+from typing import Iterator, List, NamedTuple, Optional
20+ 
21+ 
22+class Receiver(NamedTuple):
23+ """消息接收器。"""
24+ 
25+ warn_msgs: List[str]
26+ err_msgs: List[str]
27+ 
28+ 
29+def parse_version_line(line: str) -> str:
30+ """解析版本行。"""
31+ version = line.strip().split("=", maxsplit=1)[1]
32+ return version.split("-")[0]
33+ 
34+ 
35+def read_pkg_version(
36+ recv: Receiver, ascend_install_path: str, name: str
37+) -> Optional[str]:
38+ """读取包版本。"""
39+ filepath = os.path.join(ascend_install_path, "share", "info", name, "version.info")
40+ if not os.path.isfile(filepath):
41+ recv.err_msgs.append(f"{filepath} does not exist in read_pkg_version!")
42+ return None
43+ 
44+ with open(filepath, encoding="utf-8") as file:
45+ for line in file:
46+ if line.startswith("Version="):
47+ return parse_version_line(line)
48+ 
49+ recv.err_msgs.append(
50+ f"The version field was not found in {filepath} in read_pkg_version!"
51+ )
52+ return None
53+ 
54+ 
55+def check_build_dep_item(version: str, dep: str) -> bool:
56+ """检查构建依赖项。"""
57+ 
58+ def split_version(version: str) -> List[int]:
59+ return [int(num) for num in version.split(".")]
60+ 
61+ def check_ge() -> bool:
62+ for req, rel in zip(dep_parts, version_parts):
63+ if req > rel:
64+ return False
65+ if rel > req:
66+ return True
67+ return True
68+ 
69+ def check_gt() -> bool:
70+ for req, rel in zip(dep_parts, version_parts):
71+ if req > rel:
72+ return False
73+ if rel > req:
74+ return True
75+ return False
76+ 
77+ def check_le() -> bool:
78+ for req, rel in zip(dep_parts, version_parts):
79+ if rel > req:
80+ return False
81+ if req > rel:
82+ return True
83+ return True
84+ 
85+ def check_lt() -> bool:
86+ for req, rel in zip(dep_parts, version_parts):
87+ if rel > req:
88+ return False
89+ if req > rel:
90+ return True
91+ return False
92+ 
93+ def check_eq() -> bool:
94+ for req, rel in zip(dep_parts, version_parts):
95+ if req != rel:
96+ return False
97+ return True
98+ 
99+ version_parts = split_version(version)
100+ 
101+ if dep.startswith(">="):
102+ dep = dep[2:]
103+ check_func = check_ge
104+ elif dep.startswith(">"):
105+ dep = dep[1:]
106+ check_func = check_gt
107+ elif dep.startswith("<="):
108+ dep = dep[2:]
109+ check_func = check_le
110+ elif dep.startswith("<"):
111+ dep = dep[1:]
112+ check_func = check_lt
113+ else:
114+ check_func = check_eq
115+ 
116+ dep_parts = split_version(dep)
117+ 
118+ if check_func is check_eq:
119+ return check_func()
120+ 
121+ if len(dep_parts) < len(version_parts):
122+ dep_parts.extend([0] * (len(version_parts) - len(dep_parts)))
123+ elif len(dep_parts) > len(version_parts):
124+ version_parts.extend([0] * (len(dep_parts) - len(version_parts)))
125+ 
126+ return check_func()
127+ 
128+ 
129+def check_build_dep(version: str, dep_info: str) -> bool:
130+ """检查构建依赖。"""
131+ 
132+ def check_range(dep: str, deps_iter: Iterator[str]) -> bool:
133+ result = check_build_dep_item(version, dep)
134+ for dep in deps_iter:
135+ if dep.startswith("<") or dep.startswith("<="):
136+ return result and check_build_dep_item(version, dep)
137+ result &= check_build_dep_item(version, dep)
138+ return result
139+ 
140+ deps = [dep.strip() for dep in dep_info.split(",")]
141+ deps_iter = iter(deps)
142+ 
143+ for dep in deps_iter:
144+ if dep.startswith(">=") or dep.startswith(">"):
145+ result = check_range(dep, deps_iter)
146+ else:
147+ result = check_build_dep_item(version, dep)
148+ if result:
149+ return True
150+ return False
151+ 
152+ 
153+def check_build_deps(recv: Receiver, ascend_install_path: str, deps: list):
154+ """检查构建依赖。"""
155+ deps_iter = iter(deps)
156+ for dep_pkg in deps_iter:
157+ dep_info = next(deps_iter)
158+ version = read_pkg_version(recv, ascend_install_path, dep_pkg)
159+ if not version:
160+ continue
161+ try:
162+ if not check_build_dep(version, dep_info):
163+ warn_msg = (
164+ "Check build dependency failed! "
165+ f"Required {dep_pkg} version is {dep_info}, but {dep_pkg} version is {version}."
166+ )
167+ recv.warn_msgs.append(warn_msg)
168+ except ValueError:
169+ err_msg = f"Check build dependency error! version is {version}, dep_info is {dep_info}."
170+ recv.err_msgs.append(err_msg)
171+ 
172+ 
173+def main():
174+ """主流程。"""
175+ parser = argparse.ArgumentParser()
176+ parser.add_argument("ascend_install_path", help="Ascend install path.")
177+ parser.add_argument(
178+ "deps",
179+ nargs="*",
180+ help="Dependency information (pairs of package name and version).",
181+ )
182+ args = parser.parse_args()
183+ 
184+ logging.basicConfig(
185+ format=f"{os.path.basename(__file__)}: %(levelname)s: %(message)s"
186+ )
187+ if len(args.deps) % 2 != 0:
188+ logging.error("The deps argument must contain an even number of elements!")
189+ return False
190+ 
191+ recv = Receiver([], [])
192+ check_build_deps(recv, args.ascend_install_path, args.deps)
193+ 
194+ if recv.warn_msgs:
195+ for warn_msg in recv.warn_msgs:
196+ logging.warning(warn_msg)
197+ 
198+ if recv.err_msgs:
199+ for err_msg in recv.err_msgs:
200+ logging.error(err_msg)
201+ return False
202+ 
203+ return True
204+ 
205+ 
206+if __name__ == "__main__":
207+ if not main():
208+ sys.exit(1)
Ascripts/generate_version_info.py+71-0
@@ -0,0 +1,71 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# -----------------------------------------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# -----------------------------------------------------------------------------------------------------------
12+ 
13+"""生成version.info文件。"""
14+ 
15+import argparse
16+import logging
17+import os
18+import sys
19+from datetime import datetime, timedelta, timezone
20+from typing import Iterable, Iterator
21+ 
22+ 
23+def gen_version_info_content(version: str, deps: Iterable[str]) -> Iterator[str]:
24+ """生成version.info文件内容。"""
25+ yield f"Version={version}"
26+ yield "version_dir=cann"
27+ deps_iter = iter(deps)
28+ for dep_pkg in deps_iter:
29+ dep_info = next(deps_iter)
30+ yield f'required_package_{dep_pkg}_version="{dep_info}"'
31+ if os.environ.get("tagInfo"):
32+ tag_info = os.environ.get("tagInfo")
33+ timestamp = "_".join(tag_info.split("_")[-3:-1])
34+ else:
35+ timestamp = datetime.now(timezone(timedelta(hours=8))).strftime(
36+ "%Y%m%d_%H%M%S%f"
37+ )[:-3]
38+ yield f"timestamp={timestamp}"
39+ yield "" # for last \n
40+ 
41+ 
42+def main():
43+ """主流程。"""
44+ parser = argparse.ArgumentParser()
45+ parser.add_argument("version", help="Version number.")
46+ parser.add_argument(
47+ "deps",
48+ nargs="*",
49+ help="Dependency information (pairs of package name and version)",
50+ )
51+ parser.add_argument("--output", required=True, help="Output file path.")
52+ args = parser.parse_args()
53+ 
54+ logging.basicConfig(
55+ format=f"{os.path.basename(__file__)}: %(levelname)s: %(message)s"
56+ )
57+ 
58+ if len(args.deps) % 2 != 0:
59+ logging.error("The deps argument must contain an even number of elements!")
60+ return False
61+ 
62+ content = "\n".join(gen_version_info_content(args.version, args.deps))
63+ with open(args.output, "w", encoding="utf-8") as file:
64+ file.write(content)
65+ 
66+ return True
67+ 
68+ 
69+if __name__ == "__main__":
70+ if not main():
71+ sys.exit(1)
Ascripts/package/common/__init__.py+11-0
@@ -0,0 +1,11 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
Ascripts/package/common/cfg/path.cfg+2-0
@@ -0,0 +1,2 @@
1+CUSTOM_DATA_PATH=$HOME/Ascend/latest/data/
2+CUSTOM_CONF_PATH=$HOME/Ascend/latest/conf/
Ascripts/package/common/py/__init__.py+11-0
@@ -0,0 +1,11 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
Ascripts/package/common/py/filelist.py+511-0
@@ -0,0 +1,511 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
12+ 
13+"""filelist相关类。"""
14+ 
15+import itertools
16+import os
17+from enum import IntEnum
18+from collections import Counter
19+from functools import partial
20+from itertools import chain, repeat
21+from operator import and_, attrgetter, contains, itemgetter, lt, methodcaller, ne, not_
22+from typing import Callable, Iterator, List, NamedTuple, Set, Tuple
23+ 
24+from .utils.pkg_utils import (TOP_DIR, FilelistError, GenerateFilelistError,
25+ conditional_apply, pairwise, swap_args, config_feature_to_string)
26+from .utils.funcbase import (any_, constant, dispatch, identity, invoke, pipe, side_effect, star_apply)
27+from .utils.comm_log import CommLog
28+ 
29+ 
30+class FileItem(NamedTuple):
31+ """文件条目"""
32+ module: str
33+ operation: str
34+ relative_path_in_pkg: str
35+ relative_install_path: str
36+ is_in_docker: str
37+ permission: str
38+ owner_group: str
39+ install_type: str
40+ softlink: List[str]
41+ feature: Set[str]
42+ is_common_path: str
43+ configurable: str
44+ hash_value: str
45+ block: str
46+ pkg_inner_softlink: List[str]
47+ chip: Set[str]
48+ is_dir: bool
49+ 
50+ 
51+def create_file_item(*args, **kwargs) -> FileItem:
52+ """创建文件条目。"""
53+ file_item = FileItem(*args, **kwargs)
54+ 
55+ if not isinstance(file_item.feature, set):
56+ raise TypeError('The feature parameter should be a set.')
57+ if not isinstance(file_item.chip, set):
58+ raise TypeError('The chip parameter should be a set.')
59+ if not isinstance(file_item.softlink, list):
60+ raise TypeError('The softlink parameter should be a list.')
61+ if not isinstance(file_item.pkg_inner_softlink, list):
62+ raise TypeError('The pkg_inner_softlink parameter should be a list.')
63+ 
64+ return file_item
65+ 
66+ 
67+# 文件列表
68+FileList = List[FileItem]
69+ 
70+ 
71+def soft_links_to_string(soft_links: List[str]) -> str:
72+ """软链接转换为字符串。"""
73+ if not soft_links:
74+ return 'NA'
75+ return ';'.join(soft_links)
76+ 
77+ 
78+def file_item_to_string(item: FileItem) -> str:
79+ """文件条目转换为字符串。"""
80+ return ','.join([
81+ item.module, item.operation, item.relative_path_in_pkg, item.relative_install_path,
82+ item.is_in_docker, item.permission, item.owner_group, item.install_type,
83+ soft_links_to_string(item.softlink), config_feature_to_string(item.feature),
84+ item.is_common_path, item.configurable, item.hash_value, item.block,
85+ soft_links_to_string(item.pkg_inner_softlink), config_feature_to_string(item.chip)
86+ ])
87+ 
88+ 
89+def get_filelist_header_string() -> str:
90+ """获取文件列表表头。"""
91+ return ','.join([
92+ 'module', 'operation', 'relative_path_in_pkg', 'relative_install_path',
93+ 'is_in_docker', 'permission', 'owner:group', 'install_type',
94+ 'softlink', 'feature', 'is_common_path', 'configurable', 'hash',
95+ 'block', 'pkg_inner_softlink', 'chip'
96+ ])
97+ 
98+ 
99+def get_soft_links_not_in_common_paths(filelist: FileList, target_env: str) -> Iterator[List[str]]:
100+ for file_item_t in filelist:
101+ if file_item_t.relative_install_path.startswith(target_env):
102+ for softlink in file_item_t.softlink:
103+ if not softlink.startswith(target_env):
104+ yield softlink
105+ 
106+ 
107+def fill_is_common_path(filelist: FileList, target_env: str) -> Iterator[FileItem]:
108+ """填充文件条目中是否为公共目录字段。"""
109+ soft_links = set(get_soft_links_not_in_common_paths(filelist, target_env))
110+ for file_item in filelist:
111+ if file_item.relative_install_path.startswith(target_env):
112+ yield file_item._replace(is_common_path='Y')
113+ else:
114+ is_soft_links_prefix = map(
115+ methodcaller('startswith', f'{file_item.relative_install_path}/'), soft_links
116+ )
117+ if any(is_soft_links_prefix):
118+ yield file_item._replace(is_common_path='YY')
119+ else:
120+ yield file_item
121+ 
122+ 
123+def is_relative_install_path(path: str) -> bool:
124+ """是否为相对路径。"""
125+ if path.startswith('/'):
126+ return False
127+ return True
128+ 
129+ 
130+def is_specific_operations(file_item: FileItem, operations: List[str]) -> bool:
131+ """是否为特定的操作类型。"""
132+ if file_item.operation in operations:
133+ return True
134+ return False
135+ 
136+ 
137+def is_specific_install_type(file_item: FileItem, install_types: Set[str]) -> bool:
138+ """是否为特定的安装类型。"""
139+ item_install_types = set(file_item.install_type.split(';'))
140+ if 'all' in item_install_types:
141+ return True
142+ if item_install_types & install_types:
143+ return True
144+ return False
145+ 
146+ 
147+def get_install_path_dirs(install_path: str) -> Iterator[str]:
148+ """获取安装路径父目录。"""
149+ install_path = os.path.dirname(install_path)
150+ while install_path not in ('', '/'):
151+ yield install_path
152+ install_path = os.path.dirname(install_path)
153+ 
154+ 
155+def get_missing_dir_set(filelist: FileList) -> Set[str]:
156+ """获取缺失目录集合。
157+ 
158+ 文件列表可能出现某一级目录缺失情况。
159+ 如配置了file_info:aaa/bbb/ccc.txt,但只配置了dir_info:aaa,
160+ 那么缺失dir_info:aaa/bbb
161+ """
162+ parent_dirs: Set[str] = invoke(
163+ pipe(
164+ dispatch(
165+ pipe(
166+ partial(
167+ filter,
168+ partial(is_specific_operations, operations={'copy', 'copy_entity'}),
169+ ),
170+ partial(map, attrgetter('relative_install_path')),
171+ partial(filter, is_relative_install_path),
172+ set,
173+ partial(map, get_install_path_dirs),
174+ chain.from_iterable,
175+ ),
176+ pipe(
177+ partial(map, attrgetter('softlink')),
178+ chain.from_iterable,
179+ partial(
180+ filter,
181+ pipe(
182+ dispatch(
183+ bool,
184+ is_relative_install_path,
185+ partial(ne, 'NA'),
186+ ),
187+ all
188+ )
189+ ),
190+ set,
191+ partial(map, get_install_path_dirs),
192+ chain.from_iterable,
193+ ),
194+ pipe(
195+ partial(map, attrgetter('pkg_inner_softlink')),
196+ chain.from_iterable,
197+ partial(
198+ filter,
199+ pipe(
200+ dispatch(
201+ bool,
202+ partial(ne, 'NA'),
203+ ),
204+ all
205+ )
206+ ),
207+ set,
208+ partial(map, get_install_path_dirs),
209+ chain.from_iterable,
210+ ),
211+ ),
212+ chain.from_iterable,
213+ set,
214+ ),
215+ filelist
216+ )
217+ mkdir_installs: Set[str] = {
218+ file_item.relative_install_path
219+ for file_item in filter(
220+ partial(is_specific_operations, operations={'mkdir'}),
221+ filelist
222+ )
223+ if is_relative_install_path(file_item.relative_install_path)
224+ }
225+ 
226+ mkdir_parent_dirs: Set[str] = set(
227+ itertools.chain.from_iterable(
228+ map(get_install_path_dirs, mkdir_installs)
229+ )
230+ )
231+ 
232+ missing_dir_set = sorted((parent_dirs | mkdir_parent_dirs) - mkdir_installs)
233+ return set(missing_dir_set)
234+ 
235+ 
236+def print_missing_dir_set(missing_dir_set: Set[str], in_msg: str = None) -> Set[str]:
237+ """打印缺失目录集合。"""
238+ if in_msg:
239+ tail_msg = f' {in_msg}'
240+ else:
241+ tail_msg = ''
242+ for path in sorted(missing_dir_set):
243+ CommLog.cilog_error(f'missing dir info path "{path}"{tail_msg}')
244+ return missing_dir_set
245+ 
246+ 
247+def print_unsafe_paths(unsafe_paths: Tuple[str, ...]) -> Tuple[str, ...]:
248+ """打印非安全路径。"""
249+ for path in unsafe_paths:
250+ CommLog.cilog_error(f'unsafe path "{path}" in move scene.')
251+ return unsafe_paths
252+ 
253+ 
254+# 获取filelist中所有的特性集合
255+get_features_in_filelist = pipe(
256+ partial(map, attrgetter('feature')),
257+ chain.from_iterable, # 展开集合序列为元素序列
258+ set, # 去重
259+ partial(filter, partial(ne, 'comm')), # 排除comm特性
260+ set,
261+)
262+ 
263+# 获取filelist中所有的芯片集合
264+get_chips_in_filelist = pipe(
265+ partial(map, attrgetter('chip')),
266+ chain.from_iterable, # 展开集合序列为元素序列
267+ set, # 去重
268+)
269+ 
270+ 
271+def check_features_in_filelist(features: Set[str], filelist: FileList) -> Set[str]:
272+ """检查文件列表中特性配置目录规范。"""
273+ return invoke(
274+ pipe(
275+ # 过滤指定features的file_item
276+ partial(
277+ filter,
278+ pipe(attrgetter('feature'), partial(and_, features), bool)
279+ ),
280+ list,
281+ get_missing_dir_set,
282+ partial(print_missing_dir_set, in_msg=f'in features {features}'),
283+ ),
284+ filelist
285+ )
286+ 
287+ 
288+def check_chip_in_filelist(chip: str, filelist: FileList) -> Set[str]:
289+ """检查文件列表中芯片配置目录规范。"""
290+ return invoke(
291+ pipe(
292+ # 过滤指定chip的file_item
293+ partial(
294+ filter,
295+ any_(
296+ pipe(
297+ attrgetter('chip'), not_
298+ ), # 没有配置chip
299+ pipe(
300+ attrgetter('chip'), partial(swap_args(contains), chip), bool
301+ ), # 配置了指定chip
302+ ),
303+ ),
304+ list,
305+ get_missing_dir_set,
306+ partial(print_missing_dir_set, in_msg=f'in chip {chip}'),
307+ ),
308+ filelist
309+ )
310+ 
311+ 
312+check_filelist_features = any_(
313+ pipe(
314+ dispatch(
315+ pipe(
316+ get_features_in_filelist,
317+ # 对于每个feature,与comm组成一个set
318+ partial(map, lambda x: {x, 'comm'}),
319+ # 此时为feature集合序列
320+ ),
321+ repeat, # 重复filelist
322+ ),
323+ tuple,
324+ star_apply(zip),
325+ # 此时为元组序列,元组的第1个元素是过滤的feature集合,第2个元素是filelist
326+ partial(itertools.starmap, check_features_in_filelist),
327+ # 此时为集合序列;合并为一个集合
328+ chain.from_iterable,
329+ set,
330+ ),
331+ pipe(
332+ dispatch(
333+ get_chips_in_filelist,
334+ repeat, # 重复filelist
335+ ),
336+ tuple,
337+ star_apply(zip),
338+ # 此时为元组序列,元组的第1个元素是chip集合,第2个元素是filelist
339+ partial(itertools.starmap, check_chip_in_filelist),
340+ # 此时为集合序列;合并为一个集合
341+ chain.from_iterable,
342+ set,
343+ )
344+)
345+ 
346+ 
347+# 检查move是否安全,是否存在同一个源路径被mv多次
348+check_move_safe = pipe(
349+ partial(
350+ filter,
351+ partial(is_specific_operations, operations={'copy', 'copy_entity', 'move'}),
352+ ),
353+ partial(map, attrgetter('relative_path_in_pkg')),
354+ Counter,
355+ methodcaller('items'),
356+ partial(filter, pipe(itemgetter(1), partial(lt, 1))),
357+ partial(map, itemgetter(0)),
358+ tuple,
359+ print_unsafe_paths,
360+)
361+ 
362+ 
363+def check_filelist(filelist: FileList, check_features: bool, check_move: bool):
364+ """检查文件列表是否符合规范。"""
365+ if check_features:
366+ check_features_func = check_filelist_features
367+ else:
368+ check_features_func = constant(set())
369+ 
370+ if check_move:
371+ check_move_func = check_move_safe
372+ else:
373+ check_move_func = constant(tuple())
374+ 
375+ # 此处使用any_,短路部分报错
376+ check_func = any_(
377+ pipe(
378+ get_missing_dir_set,
379+ print_missing_dir_set,
380+ ),
381+ pipe(
382+ partial(filter, partial(is_specific_install_type, install_types={'run'})),
383+ list,
384+ get_missing_dir_set,
385+ partial(print_missing_dir_set, in_msg='in run install type'),
386+ ),
387+ check_features_func,
388+ check_move_func,
389+ )
390+ missing = check_func(filelist)
391+ 
392+ if missing:
393+ raise FilelistError()
394+ 
395+ 
396+def get_common_path(args: List[str]) -> str:
397+ """公共路径前缀。"""
398+ try:
399+ return os.path.commonpath(args)
400+ except ValueError:
401+ return ''
402+ 
403+ 
404+class FileItemRelation(IntEnum):
405+ """文件条目之间的关系。"""
406+ NOT_NESTED = 0 # 不是嵌套文件
407+ NESTED = 1 # 嵌套文件
408+ SAME = 2 # 相同文件
409+ 
410+ 
411+def is_nested_file_item(item: FileItem, base_item: FileItem) -> FileItemRelation:
412+ """是否为嵌套的文件。"""
413+ if base_item is None:
414+ return FileItemRelation.NOT_NESTED
415+ 
416+ if item == base_item:
417+ return FileItemRelation.SAME
418+ 
419+ install_path = item.relative_install_path
420+ base_install_path = base_item.relative_install_path
421+ 
422+ common_install_path = get_common_path([install_path, base_install_path])
423+ if common_install_path != base_install_path:
424+ return FileItemRelation.NOT_NESTED
425+ 
426+ pkg_path = item.relative_path_in_pkg
427+ base_pkg_path = base_item.relative_path_in_pkg
428+ 
429+ install_rel_path = os.path.relpath(install_path, base_install_path)
430+ pkg_rel_path = os.path.relpath(pkg_path, base_pkg_path)
431+ if install_rel_path != pkg_rel_path:
432+ # 确保打包与安装相对路径一致
433+ raise FilelistError(f'nested paths {item} and {base_item} are illegal.')
434+ return FileItemRelation.NESTED
435+ 
436+ 
437+def found_nested_file_item(item: FileItem, base_item: FileItem):
438+ """发现嵌套元素。"""
439+ raise FilelistError(f'found nested paths {item} and {base_item}!')
440+ 
441+ 
442+def convert_nested_path_in_filelist(filelist: FileList):
443+ """filelist中嵌套路径元素转为del。"""
444+ pre_item = None
445+ for item in filelist:
446+ ret = is_nested_file_item(item, pre_item)
447+ if ret == FileItemRelation.NESTED:
448+ yield item._replace(operation='del')
449+ elif any((
450+ ret == FileItemRelation.NOT_NESTED,
451+ (ret == FileItemRelation.SAME and not item.is_dir)
452+ )):
453+ yield item
454+ pre_item = item
455+ 
456+ 
457+# 检查文件列表中的嵌套路径。入参: filelist
458+check_nested_path_in_filelist = pipe(
459+ partial(filter, partial(is_specific_operations, operations={'copy', 'copy_entity'})),
460+ partial(sorted, key=attrgetter('relative_install_path')),
461+ pairwise,
462+ partial(
463+ map,
464+ conditional_apply(star_apply(is_nested_file_item), star_apply(found_nested_file_item))
465+ ),
466+ list,
467+)
468+ 
469+ 
470+# 变换文件列表中嵌套路径。入参: filelist
471+transform_nested_path_in_filelist = pipe(
472+ dispatch(
473+ partial(
474+ itertools.filterfalse, partial(is_specific_operations, operations={'copy'})
475+ ),
476+ pipe(
477+ partial(filter, partial(is_specific_operations, operations={'copy'})),
478+ partial(sorted, key=attrgetter('relative_install_path')),
479+ convert_nested_path_in_filelist
480+ ),
481+ ),
482+ chain.from_iterable,
483+ list,
484+ side_effect(check_nested_path_in_filelist),
485+)
486+ 
487+ 
488+def generate_filelist(filelist: FileList, filename: str):
489+ """生成文件列表文件。"""
490+ content_list = list(
491+ itertools.chain(
492+ [get_filelist_header_string()],
493+ [file_item_to_string(item) for item in filelist]
494+ )
495+ )
496+ content = '\n'.join(content_list)
497+ filepath = os.path.join(TOP_DIR, "build", filename)
498+ try:
499+ with open(filepath, 'w', encoding='utf-8') as file:
500+ file.write(content)
501+ # filelist.csv文件末尾补充一个换行符
502+ file.write('\n')
503+ except OSError as ex:
504+ raise GenerateFilelistError(filename) from ex
505+ 
506+ 
507+def get_transform_nested_path_func(parallel: bool) -> Callable[[FileList], FileList]:
508+ """获取转换嵌套路径函数。"""
509+ if parallel:
510+ return transform_nested_path_in_filelist
511+ return identity
Ascripts/package/common/py/merge_binary_info_config.py+136-0
@@ -0,0 +1,136 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# -----------------------------------------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# -----------------------------------------------------------------------------------------------------------
12+ 
13+"""合并算子binary_info_config.json。"""
14+ 
15+import argparse
16+import json
17+import os
18+import sys
19+from typing import List
20+ 
21+ 
22+def load_json_file(json_file: str):
23+ """加载json文件。"""
24+ with open(json_file, encoding='utf-8') as file:
25+ json_content = json.load(file)
26+ return json_content
27+ 
28+ 
29+def save_json_file(output_file: str, content):
30+ """保存json文件。"""
31+ output_dir = os.path.dirname(output_file)
32+ if not os.path.exists(output_dir):
33+ os.makedirs(output_dir, exist_ok=True)
34+ 
35+ with open(output_file, 'w', encoding='utf-8') as file:
36+ json.dump(content, file, ensure_ascii=True, indent=2)
37+ 
38+ 
39+def update_config(base_content, update_content):
40+ """合并配置。"""
41+ merged = {}
42+ base_ops = list(base_content)
43+ update_ops = list(update_content)
44+ all_ops = list(dict.fromkeys(base_ops + update_ops))
45+ 
46+ for op in all_ops:
47+ base_op = base_content.get(op, {})
48+ update_op = update_content.get(op, {})
49+ is_base_dict = isinstance(base_op, dict)
50+ is_update_dict = isinstance(update_op, dict)
51+ 
52+ if not (is_base_dict and is_update_dict):
53+ merged[op] = _select_value(update_content, base_content, op)
54+ continue
55+ 
56+ merged[op] = _merge_operator_config(base_op, update_op)
57+ 
58+ return merged
59+ 
60+ 
61+def _select_value(update_content, base_content, key):
62+ """选择 update 或 base 中的值。"""
63+ if key in update_content:
64+ return update_content[key]
65+ return base_content.get(key)
66+ 
67+ 
68+def _merge_operator_config(base_op, update_op):
69+ """合并单个算子的配置,保留字段顺序并拼接 binaryList。"""
70+ base_fields = list(base_op)
71+ update_fields = list(update_op)
72+ fields_order = list(dict.fromkeys(base_fields + update_fields))
73+ new_op = {}
74+ 
75+ has_binary_list = ("binaryList" in base_op) or ("binaryList" in update_op)
76+ 
77+ if has_binary_list:
78+ combined_bl = _merge_binary_list(base_op, update_op)
79+ for field in fields_order:
80+ if field == "binaryList":
81+ new_op[field] = combined_bl
82+ else:
83+ new_op[field] = _get_merged_field_value(update_op, base_op, field)
84+ else:
85+ for field in fields_order:
86+ new_op[field] = _get_merged_field_value(update_op, base_op, field)
87+ 
88+ return new_op
89+ 
90+ 
91+def _merge_binary_list(base_op, update_op):
92+ """拼接 binaryList,保持 base 在前、update 在后。"""
93+ base_bl = base_op.get("binaryList")
94+ update_bl = update_op.get("binaryList")
95+ base_list = base_bl if isinstance(base_bl, list) else []
96+ update_list = update_bl if isinstance(update_bl, list) else []
97+ return base_list + update_list
98+ 
99+ 
100+def _get_merged_field_value(update_op, base_op, field):
101+ """获取字段合并后的值:update 优先,fallback 到 base。"""
102+ if field in update_op:
103+ return update_op[field]
104+ return base_op.get(field)
105+ 
106+ 
107+def parse_args(argv: List[str]):
108+ """入参解析。"""
109+ parser = argparse.ArgumentParser()
110+ parser.add_argument('--base-file',
111+ required=True,
112+ help='the basic binary_info_config file')
113+ parser.add_argument('--update-file',
114+ required=True,
115+ help='the update binary_info_config file')
116+ parser.add_argument('--output-file',
117+ required=True,
118+ type=os.path.realpath,
119+ help='the output binary_info_config file')
120+ args = parser.parse_args(argv)
121+ return args
122+ 
123+ 
124+def main(argv: List[str]) -> bool:
125+ """主流程。"""
126+ args = parse_args(argv)
127+ base_content = load_json_file(args.base_file)
128+ update_content = load_json_file(args.update_file)
129+ result = update_config(base_content, update_content)
130+ save_json_file(args.output_file, result)
131+ return True
132+ 
133+ 
134+if __name__ == '__main__':
135+ if not main(sys.argv[1:]): # pragma: no cover
136+ sys.exit(1) # pragma: no cover
Ascripts/package/common/py/packer.py+220-0
@@ -0,0 +1,220 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
12+ 
13+import os
14+import shutil
15+import subprocess
16+from argparse import Namespace
17+from itertools import chain
18+from subprocess import PIPE, STDOUT
19+from typing import Callable, Dict, List, NamedTuple, Optional, Tuple, Union
20+ 
21+from .utils.comm_log import CommLog
22+from .utils.pkg_utils import CompressError
23+ 
24+ 
25+class PackageName:
26+ """包名。"""
27+ 
28+ def __init__(self,
29+ package_attr,
30+ args: Namespace,
31+ version: str):
32+ self.product_name = package_attr.get('product_name')
33+ self.chip_name = args.chip_name or package_attr.get('chip_name')
34+ self.suffix = args.suffix or package_attr.get('suffix')
35+ self.func_name = get_func_name(args.func_name, package_attr)
36+ self.chip_plat = package_attr.get('chip_plat')
37+ self.deploy_type = package_attr.get('deploy_type')
38+ self.version = version.lower()
39+ self.not_in_name_list = args.not_in_name.split(",")
40+ self.os_arch = args.os_arch
41+ self.package_suffix = args.package_suffix
42+ self.ext_name = args.ext_name
43+ if args.pkg_name_style == 'underline':
44+ self.name_sep = '_'
45+ else:
46+ self.name_sep = '-'
47+ 
48+ def get_attribute(self, name: str) -> Optional[str]:
49+ """获取属性。"""
50+ if name in self.not_in_name_list:
51+ return None
52+ return getattr(self, name)
53+ 
54+ def getvalue(self) -> str:
55+ product_name = self.get_attribute('product_name')
56+ chip_name = self.get_attribute('chip_name')
57+ func_name = self.get_attribute('func_name')
58+ version = self.get_attribute('version')
59+ os_arch = self.get_attribute('os_arch')
60+ chip_plat = self.get_attribute('chip_plat')
61+ deploy_type = self.get_attribute('deploy_type')
62+ ext_name = self.get_attribute('ext_name')
63+ package_suffix = "debug" if self.package_suffix == "debug" else None
64+ 
65+ region1 = "-".join(filter(None, [product_name, remove_ascend(chip_name), func_name]))
66+ region2 = ".".join(filter(None, [version]))
67+ region3 = "-".join(filter(None, [os_arch, chip_plat, deploy_type, package_suffix, ext_name]))
68+ package_name = "_".join(filter(None, [region1, region2, region3]))
69+ 
70+ return f"{package_name}.{self.suffix}"
71+ 
72+ 
73+class MakeselfPkgParams(NamedTuple):
74+ """run包打包参数。"""
75+ package_name: str
76+ comments: str
77+ makeself_tool: Optional[str] = None
78+ makeself_header: Optional[str] = None
79+ help_info: Optional[str] = None
80+ source_target: Optional[str] = None
81+
82+ install_script: Optional[str] = None
83+ independent_pkg: Optional[bool] = False
84+ cleanup: Optional[str] = None
85+ 
86+ 
87+def remove_ascend(text):
88+ if text is None:
89+ return None
90+ text_lower = text.lower()
91+ if text_lower == "ascend910_93":
92+ return "A3"
93+ if "ascend" in text_lower:
94+ return text_lower.replace("ascend", "")
95+ return text_lower
96+ 
97+ 
98+def get_func_name(func_name: str, package_attr) -> str:
99+ """获取包func_name。"""
100+ return func_name or package_attr.get('func_name')
101+ 
102+ 
103+def get_compress_tool() -> str:
104+ tools = ["pigz", "gzip", "bzip2", "xz"]
105+ for tool in tools:
106+ path = shutil.which(tool)
107+ if path:
108+ return "--" + tool
109+ CommLog.cilog_error("The system does not come with a compression tool pre-installed."
110+ "Please ensure at least one of the folllowing compression tools is available: %s", tools)
111+ return ""
112+ 
113+ 
114+def get_compress_format() -> str:
115+ tar_format = "gnu"
116+ path = shutil.which("bsdtar")
117+ if path:
118+ tar_format = "ustar"
119+ return tar_format
120+ 
121+ 
122+def compose_makeself_command(params: MakeselfPkgParams) -> str:
123+ """组装makeself包打包命令。"""
124+ 
125+ def get_cleanup_commands() -> List[str]:
126+ if params.cleanup:
127+ return ['--cleanup', params.cleanup]
128+ return []
129+ independent_pkg = params.independent_pkg
130+ compress_tool = get_compress_tool()
131+ tar_format = get_compress_format()
132+ if independent_pkg:
133+ commands = chain(
134+ [
135+ 'TMPDIR=$pwd', params.makeself_tool, "--header", params.makeself_header,
136+ "--help-header", params.help_info, compress_tool, '--complevel', '4',
137+ '--nomd5', '--sha256', '--nooverwrite', '--chown', '--tar-format', tar_format,
138+ '--tar-extra', '--numeric-owner', '--tar-quietly'
139+ ],
140+ get_cleanup_commands(),
141+ [params.source_target, params.package_name, params.comments, params.install_script]
142+ )
143+ else:
144+ commands = chain(
145+ [
146+ compress_tool, '--complevel', '4',
147+ '--nomd5', '--sha256', '--nooverwrite', '--chown', '--tar-format', tar_format,
148+ '--tar-extra', '--numeric-owner', '--tar-quietly'
149+ ],
150+ get_cleanup_commands(),
151+ [params.package_name, params.comments]
152+ )
153+
154+ command = ' '.join(commands)
155+ return command
156+ 
157+ 
158+def create_makeself_pkg_params_factory(source_target: str,
159+ package_name: str,
160+ comments: str
161+ ) -> Callable[[str, dict, bool], MakeselfPkgParams]:
162+ """创建Makeself打包参数工厂。"""
163+ 
164+ def create_makeself_pkg_params(makeself_dir: str,
165+ package_attr: Dict,
166+ independent_pkg=False) -> MakeselfPkgParams:
167+ """创建Makeself打包参数。"""
168+ cleanup = package_attr.get('cleanup')
169+ 
170+ if independent_pkg:
171+ install_script = str(package_attr.get('install_script'))
172+ help_info = str(package_attr.get('help'))
173+ makeself_tool = os.path.join(makeself_dir, 'makeself.sh')
174+ makeself_header = os.path.join(makeself_dir, 'makeself-header.sh')
175+ params = MakeselfPkgParams(
176+ package_name=package_name,
177+ comments=comments,
178+ makeself_tool=makeself_tool,
179+ makeself_header=makeself_header,
180+ help_info=help_info,
181+ source_target=source_target,
182+
183+ install_script=install_script,
184+ independent_pkg=independent_pkg,
185+ cleanup=cleanup,
186+ )
187+ else:
188+ params = MakeselfPkgParams(
189+ package_name=package_name,
190+ comments=comments,
191+ cleanup=cleanup,
192+ )
193+ return params
194+ return create_makeself_pkg_params
195+ 
196+ 
197+def create_run_package_command(params: MakeselfPkgParams
198+ ) -> Tuple[Optional[str], Optional[str]]:
199+ """
200+ 功能描述: 组装打run包命令
201+ 返回值: command
202+ """
203+ return compose_makeself_command(params), None
204+ 
205+ 
206+def exec_pack_cmd(delivery_dir: str,
207+ pack_cmd: str,
208+ package_name: str) -> str:
209+ """执行打包命令"""
210+ if delivery_dir:
211+ cmd = f'cd {delivery_dir} && {pack_cmd}'
212+ else:
213+ cmd = pack_cmd
214+ CommLog.cilog_info("package cmd:%s", cmd)
215+ result = subprocess.run(cmd, shell=True, check=False, stdout=PIPE, stderr=STDOUT)
216+ output = result.stdout.decode()
217+ if result.returncode != 0:
218+ CommLog.cilog_error(__file__, "compress package(%s) failed! %s.", package_name, output)
219+ raise CompressError(package_name)
220+ return package_name
Ascripts/package/common/py/pkg_parser.py+1145-0
@@ -0,0 +1,1145 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
12+ 
13+import copy
14+import glob
15+import hashlib
16+import itertools
17+import re
18+import xml.etree.ElementTree as ET
19+import os
20+import sys
21+from argparse import Namespace
22+from functools import partial
23+from io import StringIO
24+from itertools import chain
25+from operator import attrgetter, itemgetter, methodcaller
26+from typing import (
27+ Any, Callable, Dict, Iterable, Iterator, List, NamedTuple, Optional, Set, Tuple, Union
28+)
29+ 
30+from .utils import pkg_utils
31+from .filelist import FileItem, FileList, fill_is_common_path
32+from .utils.pkg_utils import (
33+ ContainAsteriskError, FAIL, BLOCK_CONFIG_PATH,
34+ InstallScriptFormatError, InstallScriptNotInPackageError,
35+ BlockConfigError, VersionInfoNotExist,
36+ EnvNotSupported, IllegalVersionDir, PackageError,
37+ ParseOsArchError, config_feature_to_set,
38+ flatten, star_pipe, merge_dict, yield_if
39+)
40+from .utils.funcbase import constant, dispatch, invoke, pipe, star_apply
41+from .utils.comm_log import CommLog
42+from .version_info import (
43+ VersionInfo, VersionXml, VersionFormatNotMatch, is_multi_version
44+)
45+ 
46+# 环境变量字典
47+EnvDict = Dict[str, str]
48+ 
49+# 文件信息
50+FileInfo = Dict[str, str]
51+ 
52+# 包属性
53+PackageAttr = Dict[str, Union[str, bool]]
54+ 
55+# 生成信息
56+GenerateInfo = Dict[str, str]
57+ 
58+ 
59+class ParseOption(NamedTuple):
60+ """解析参数。"""
61+ os_arch: Optional[str]
62+ pkg_version: Optional[str]
63+ build_type: Optional[str]
64+ package_check: bool
65+ ext_name: str = ''
66+ 
67+ 
68+def parse_os_arch(os_arch: str) -> Tuple[str, str, str]:
69+ """解析系统和架构。"""
70+ match = re.match("^([a-z]+)(\\d+(\\.\\d+)*)?[.-]?(\\S*)", os_arch)
71+ if match:
72+ os_name = match.group(1)
73+ os_ver = match.group(2)
74+ if match.group(4):
75+ arch = match.group(4)
76+ else:
77+ # 如果os_arch中没有配置ARCH,ARCH默认值为aarch64
78+ arch = 'aarch64'
79+ 
80+ return os_name, os_ver, arch
81+ 
82+ raise ParseOsArchError()
83+ 
84+ 
85+def replace_env(env_dict: EnvDict, in_str: str):
86+ """替换环境变量为实际值。"""
87+ env_list = re.findall(".*?\\$\\((.*?)\\).*?", in_str)
88+ for env in env_list:
89+ if env == 'FILE':
90+ continue
91+ if env in env_dict:
92+ if env_dict[env] is not None:
93+ in_str = in_str.replace(f"$({env})", env_dict[env])
94+ else:
95+ in_str = in_str.replace(f"$({env})", '')
96+ else:
97+ raise EnvNotSupported(f"Error: {env} not supported.")
98+ return in_str
99+ 
100+ 
101+class ParseEnv(NamedTuple):
102+ """解析上下文环境。"""
103+ env_dict: EnvDict
104+ parse_option: ParseOption
105+ delivery_dir: str
106+ top_dir: str
107+ 
108+ 
109+class BlockElement(NamedTuple):
110+ """块配置。"""
111+ name: str
112+ block_conf_path: str
113+ dst_path: str
114+ chips: Set[str]
115+ features: Set[str]
116+ attrs: Dict[str, str]
117+ 
118+ 
119+# BlockElement直接透传给LoadedBlockElement的参数列表
120+BLOCK_ELEMENT_PASS_THROUGH_ARGS = ['dst_path', 'chips', 'features', 'attrs']
121+ 
122+ 
123+class LoadedBlockElement(NamedTuple):
124+ """加载后的块配置。"""
125+ root_ele: ET.Element
126+ use_move: bool
127+ dst_path: str
128+ chips: Set[str]
129+ features: Set[str]
130+ attrs: Dict[str, str]
131+ 
132+ 
133+class FileInfoParsedResult(NamedTuple):
134+ """file_info元素解析结果。"""
135+ file_info: FileInfo
136+ move_infos: List[FileInfo]
137+ dir_infos: List[Dict[str, str]]
138+ expand_infos: List[Dict[str, str]]
139+ 
140+ 
141+class BlockConfig(NamedTuple):
142+ """块配置。"""
143+ 
144+ dir_install_list: List[Dict]
145+ move_files: List[FileInfo]
146+ expand_content_list: List[Dict]
147+ package_content_list: List[Dict]
148+ generate_infos: List[GenerateInfo]
149+ 
150+ 
151+class PackerConfig(NamedTuple):
152+ """安装相关配置。"""
153+ fill_is_common_path: Callable[[FileList], Iterator[FileItem]]
154+ 
155+ 
156+class XmlConfig(NamedTuple):
157+ """安装xml配置。"""
158+ default_config: Dict[str, str]
159+ package_attr: PackageAttr
160+ version_info: VersionInfo
161+ blocks: List[BlockConfig]
162+ version: str
163+ version_xml: Optional[VersionXml]
164+ packer_config: PackerConfig
165+ 
166+ def _collect_list(self, list_name):
167+ result = []
168+ for block in self.blocks:
169+ result.extend(getattr(block, list_name))
170+ return result
171+ 
172+ @property
173+ def dir_install_list(self):
174+ return self._collect_list('dir_install_list')
175+ 
176+ @property
177+ def move_content_list(self):
178+ return self._collect_list('move_files')
179+ 
180+ @property
181+ def expand_content_list(self):
182+ return self._collect_list('expand_content_list')
183+ 
184+ @property
185+ def package_content_list(self):
186+ return self._collect_list('package_content_list')
187+ 
188+ @property
189+ def generate_infos(self) -> List[GenerateInfo]:
190+ return self._collect_list('generate_infos')
191+ 
192+ 
193+# 默认包属性
194+DEFAULT_PACKAGE_ATTR = {
195+ 'gen_version_info': True,
196+}
197+ 
198+ 
199+def parse_package_info(package_info_ele: Optional[ET.Element]) -> Dict:
200+ """解析package_info元素。"""
201+ 
202+ def get_package_info_attrs(ele: ET.Element) -> Iterator[Tuple[str, Union[str, bool]]]:
203+ # expand_asterisk: 展开配置中星号
204+ # parallel: 并行复制文件
205+ # parallel_limit: 限制并发数
206+ # package_check: 检查filelist.csv中配置目录是否完整
207+ # check_features: 检查filelist.csv中所有feature是否符合package_check
208+ # gen_version_info: 是否生成version.info文件
209+ bool_attrs = (
210+ 'expand_asterisk', 'parallel', 'parallel_limit', 'package_check', 'check_features',
211+ 'use_move', 'gen_version_info'
212+ )
213+ bool_values = ('t', 'true', 'y', 'yes')
214+ if ele.tag in bool_attrs:
215+ if ele.text.lower() in bool_values:
216+ yield ele.tag, True
217+ else:
218+ yield ele.tag, False
219+ else:
220+ yield ele.tag, ele.text
221+ 
222+ if not package_info_ele:
223+ return {}
224+ 
225+ attr = dict(
226+ chain.from_iterable(map(get_package_info_attrs, list(package_info_ele)))
227+ )
228+ 
229+ return attr
230+ 
231+ 
232+def parse_package_attr_by_args(args: Namespace) -> Dict:
233+ """通过命令行参数解析"""
234+ 
235+ def pairs():
236+ if hasattr(args, 'chip_name') and args.chip_name:
237+ yield 'chip_name', args.chip_name
238+ if hasattr(args, 'suffix') and args.suffix:
239+ yield 'suffix', args.suffix
240+ if hasattr(args, 'func_name') and args.func_name:
241+ yield 'func_name', args.func_name
242+ 
243+ return dict(pairs())
244+ 
245+ 
246+def parse_package_attr(root_ele: ET.Element, args: Namespace) -> Dict:
247+ """通过根元素解析package_info元素。"""
248+ package_info_ele = root_ele.find("package_info")
249+ return merge_dict(
250+ DEFAULT_PACKAGE_ATTR,
251+ parse_package_info(package_info_ele),
252+ parse_package_attr_by_args(args),
253+ )
254+ 
255+ 
256+def render_cann_version(a_ver: int,
257+ b_ver: int,
258+ c_ver: Optional[int],
259+ d_ver: Optional[int],
260+ e_ver: Optional[int],
261+ f_ver: Optional[int]) -> str:
262+ """渲染CANN版本号。"""
263+ buffer = StringIO()
264+ buffer.write('(')
265+ buffer.write(f'({a_ver + 1} * 100000000) + ({b_ver + 1} * 1000000)')
266+ if c_ver is not None:
267+ buffer.write(f' + ({c_ver + 1} * 10000)')
268+ if d_ver is not None:
269+ buffer.write(f' + (({d_ver + 1} * 100) + 5000)')
270+ if e_ver is not None:
271+ buffer.write(f' + ({e_ver + 1} * 100)')
272+ if f_ver is not None:
273+ buffer.write(f' + {f_ver}')
274+ buffer.write(')')
275+ return buffer.getvalue()
276+ 
277+ 
278+def render_semver(package_name: str, version: str) -> Iterator[Tuple[str, str]]:
279+ """
280+ 将语义化版本号转换为可比较的整数表达式,严格遵循SemVer规范
281+ 
282+ 排序规则:
283+ 1. 正式版本 > 所有对应预发布版本(如 8.0.5 > 8.0.5-rc.1
284+ 2. 预发布类型优先级:rc > beta > alpha > 其他类型(如 rc.1 > beta.100
285+ 3. 同类型预发布版本:序号越大优先级越高(如 alpha.10 > alpha.2
286+ 4. 多段序号比较:从左到右逐段比较(如 alpha.1.2 > alpha.1.1
287+ """
288+ expr_buffer = StringIO()
289+ expr_buffer.write('(')
290+ 
291+ # 移除构建元数据(+后面的内容不影响版本优先级)
292+ version = version.split('+')[0]
293+ 
294+ # 分离正式版本和预发布版本
295+ pre_release = None
296+ if '-' in version:
297+ release_part, pre_release = version.split('-', 1)
298+ release_part = release_part.split('.')
299+ else:
300+ release_part = version.split('.')
301+ if len(release_part) > 3:
302+ pre_release = '.'.join(release_part[3:])
303+ release_part = release_part[:3]
304+ 
305+ # 解析正式版本号(主版本.次版本.修订号)
306+ try:
307+ major, minor, patch = map(int, release_part)
308+ except (ValueError, TypeError) as ex:
309+ raise IllegalVersionDir(f"无效的版本号格式: {version}") from ex
310+ 
311+ yield f'{package_name}_VERSION_STR', f'"{version}"'
312+ yield f'{package_name}_MAJOR', str(major)
313+ yield f'{package_name}_MINOR', str(minor)
314+ yield f'{package_name}_PATCH', str(patch)
315+ # 计算基础版本值(主版本*10^7 + 次版本*10^5 + 修订号*10^3)
316+ # 预留10^3空间用于预发布版本,确保不同正式版本区间不重叠
317+ expr_buffer.write(f'({major} * 10000000) + ({minor} * 100000) + ({patch} * 1000)')
318+ 
319+ # 处理正式版本(无预发布部分)
320+ if not pre_release:
321+ expr_buffer.write(')')
322+ yield f'{package_name}_PRERELEASE', '""'
323+ yield f'{package_name}_VERSION_NUM', expr_buffer.getvalue()
324+ return
325+ 
326+ yield f'{package_name}_PRERELEASE', f'"{pre_release}"'
327+ 
328+ # 预发布类型权重(值越小优先级越高)
329+ type_weights = {
330+ 'rc': 100, # rc优先级最高
331+ 'beta': 200, # beta次之
332+ 'alpha': 300, # alpha最低
333+ }
334+ 
335+ def calc_pre_release() -> Tuple[int, int]:
336+ """计算预发布版本。"""
337+ if '.' in pre_release:
338+ pre_parts = pre_release.split('.')
339+ pre_type = pre_parts[0] # 提取预发布类型(rc/beta/alpha等)
340+ 
341+ # 提取序号部分(支持多段序号,非数字部分忽略)
342+ pre_nums = []
343+ for part in pre_parts[1:]:
344+ if part.isdigit():
345+ pre_nums.append(int(part))
346+ if not pre_nums: # 无序号时默认0
347+ pre_nums = [0]
348+ 
349+ # 未知类型权重设为400(优先级低于alpha)
350+ pre_type_weight = type_weights.get(pre_type, 400)
351+ 
352+ # 计算序号值(支持多段和多位数)
353+ num_str = ''.join(map(str, pre_nums))
354+ # 转换为整数并返回
355+ num_value = int(num_str)
356+ return pre_type_weight, num_value
357+ 
358+ for pre_type in type_weights:
359+ if pre_release.startswith(pre_type):
360+ pre_type_weight = type_weights[pre_type]
361+ num_value = int(pre_release[len(pre_type):])
362+ return pre_type_weight, num_value
363+ 
364+ return None, None
365+ 
366+ try:
367+ pre_type_weight, num_value = calc_pre_release()
368+ except (ValueError, TypeError) as ex:
369+ raise IllegalVersionDir(f"无效的预发布版本: {pre_release}") from ex
370+ 
371+ if not pre_type_weight:
372+ raise IllegalVersionDir(f"无效的预发布版本: {pre_release}")
373+ 
374+ # 预发布版本最终值 = 基础值 - 类型权重 + 序号值
375+ # 确保:预发布值 < 基础值(正式版本)
376+ expr_buffer.write(f' - {pre_type_weight} + {num_value}')
377+ expr_buffer.write(')')
378+ 
379+ yield f'{package_name}_VERSION_NUM', expr_buffer.getvalue()
380+ 
381+ 
382+def get_cann_version_info(name: str, version: str) -> Iterator[Tuple[str, str]]:
383+ """获取CANN版本号信息。"""
384+ version_info = []
385+ 
386+ # 删除字符串中的_VERSION
387+ package_name = name[:-8]
388+ 
389+ if not version:
390+ yield f'{package_name}_VERSION_STR', '"0"'
391+ return
392+ 
393+ yield from render_semver(package_name, version)
394+ 
395+ 
396+def get_default_env_items() -> Iterator[Tuple[str, str]]:
397+ """获取默认环境字典条目。"""
398+ yield 'VERSION_DIR', ''
399+ yield 'HOME', os.environ.get('HOME')
400+ 
401+ 
402+def get_env_items_by_version(version: Optional[str]) -> Iterator[Tuple[str, str]]:
403+ """根据version获取环境字典条目。"""
404+ if version:
405+ yield 'ASCEND_VER', version
406+ 
407+ version_parts = version.split('.')
408+ for idx in range(1, len(version_parts) + 1):
409+ yield f'CUR_VER[{idx}]', '.'.join(version_parts[:idx])
410+ yield 'CUR_VER', version
411+ yield 'LOWER_CUR_VER', version.lower()
412+ 
413+ 
414+def get_env_items_by_version_dir(version_dir: Optional[str]) -> Iterator[Tuple[str, str]]:
415+ """根据version_dir获取环境字典条目。"""
416+ if version_dir:
417+ yield 'VERSION_DIR', version_dir
418+ 
419+ 
420+def get_os_arch_default_env_items() -> Iterator[Tuple[str, str]]:
421+ """获取系统相关默认环境字典条目。"""
422+ yield 'OS_NAME', 'linux'
423+ yield 'OS_VER', ''
424+ yield 'ARM', 'aarch64'
425+ yield 'TARGET_ENV', '$(TARGET_ENV)'
426+ 
427+ 
428+def get_env_items_by_os_arch(os_arch: str) -> Iterator[Tuple[str, str]]:
429+ """根据os_arch获取环境字典条目。"""
430+ if os_arch:
431+ os_name, os_ver, arch = parse_os_arch(os_arch)
432+ yield 'OS_NAME', os_name
433+ yield 'OS_VER', os_ver
434+ yield 'ARCH', arch
435+ yield 'OS_ARCH', os_arch
436+ if arch in ('arm', 'sw_64'):
437+ yield 'ARM', arch
438+ else:
439+ yield 'ARM', 'aarch64'
440+ yield 'TARGET_ENV', f"{arch}-linux"
441+ else:
442+ yield from get_os_arch_default_env_items()
443+ 
444+ 
445+def get_env_items_by_timestamp(timestamp: Optional[str]) -> Iterator[Tuple[str, str]]:
446+ """根据timestamp获取环境字典条目。"""
447+ if timestamp:
448+ yield 'TIMESTAMP', timestamp
449+ yield 'TIMESTAMP_NO', timestamp.replace('_', '')
450+ else:
451+ yield 'TIMESTAMP', '0'
452+ yield 'TIMESTAMP_NO', '0'
453+ 
454+ 
455+def parse_env_dict(os_arch: str,
456+ package_attr: PackageAttr,
457+ version: Optional[str],
458+ version_dir: Optional[str],
459+ timestamp: Optional[str]) -> EnvDict:
460+ """解析环境变量字典。"""
461+ env_dict = dict(
462+ chain(
463+ get_default_env_items(),
464+ yield_if(('ARCH', package_attr.get('default_arch')), itemgetter(1)),
465+ get_env_items_by_os_arch(os_arch),
466+ get_env_items_by_version(version),
467+ get_env_items_by_version_dir(version_dir),
468+ yield_if(('VERSION_DIR', version_dir), constant(version_dir)),
469+ get_env_items_by_timestamp(timestamp),
470+ )
471+ )
472+ 
473+ return env_dict
474+ 
475+ 
476+def get_timestamp(args: Namespace) -> Optional[str]:
477+ """获取触发时间戳。"""
478+ if 'tag' not in args:
479+ return None
480+ 
481+ tag = args.tag
482+ if tag:
483+ timestamp_re = r"\d{8}_\d{9}"
484+ timestamp_list = re.findall(timestamp_re, tag)
485+ if not timestamp_list:
486+ raise PackageError("The {} format is incorrect.".format(tag))
487+ timestamp = timestamp_list[-1]
488+ else:
489+ timestamp = None
490+ return timestamp
491+ 
492+ 
493+def extract_element_attrib(ele: ET.Element) -> Dict:
494+ """提取元素属性。"""
495+ return ele.attrib.copy()
496+ 
497+ 
498+def extract_generate_info_content(generate_info_ele: ET.Element, env_dict: EnvDict) -> Dict:
499+ """提取生成信息内容。"""
500+ file_content = {
501+ sub_item.tag: replace_env(env_dict, sub_item.text)
502+ for sub_item in list(generate_info_ele)
503+ }
504+ return {
505+ 'content': file_content
506+ }
507+ 
508+ 
509+def parse_generate_infos_by_loaded_block(loaded_block: LoadedBlockElement,
510+ default_config: Dict[str, str],
511+ env_dict: EnvDict) -> List[Dict]:
512+ """根据根元素解析生成信息列表。"""
513+ return invoke(
514+ pipe(
515+ partial(
516+ map, pipe(
517+ dispatch(
518+ pipe(
519+ extract_element_attrib,
520+ partial(merge_dict, default_config),
521+ partial(evaluate_info, loaded_block=loaded_block, env_dict=env_dict),
522+ ),
523+ partial(extract_generate_info_content, env_dict=env_dict),
524+ ),
525+ star_apply(merge_dict),
526+ )
527+ ),
528+ list,
529+ ),
530+ loaded_block.root_ele.findall('generate_info')
531+ )
532+ 
533+ 
534+def join_pkg_inner_softlink(link_str_list: List[str]) -> str:
535+ """合并pkg_inner_softlink"""
536+ path = "/".join(link_str_list)
537+ return os.path.normpath(path)
538+ 
539+ 
540+def check_contain_asterisk(value: str) -> bool:
541+ """检查串是否包含星号。"""
542+ if '*' in value:
543+ return True
544+ return False
545+ 
546+ 
547+def check_value(value: str,
548+ package_check: bool,
549+ package_attr: PackageAttr):
550+ """检查元素value属性。"""
551+ if package_check and package_attr.get('suffix') == 'run':
552+ if check_contain_asterisk(value):
553+ raise ContainAsteriskError(value)
554+ 
555+ 
556+def get_dst_prefix(file_info: FileInfo, env: ParseEnv) -> str:
557+ """获取文件的前缀。"""
558+ return os.path.join(env.delivery_dir, file_info['dst_path'])
559+ 
560+ 
561+def get_dst_target(file_info: FileInfo, env: ParseEnv) -> str:
562+ """获取文件的实际路径。"""
563+ dst_prefix = get_dst_prefix(file_info, env)
564+ return os.path.join(dst_prefix, os.path.basename(file_info.get('value')))
565+ 
566+ 
567+def make_hash(filepath: str) -> str:
568+ """计算文件的hash(sha256)值。"""
569+ sha256_hash = hashlib.sha256()
570+ with open(filepath, "rb") as file:
571+ sha256_hash.update(file.read())
572+ 
573+ return sha256_hash.hexdigest()
574+ 
575+ 
576+def config_hash(parsed_result: FileInfoParsedResult, env: ParseEnv):
577+ """配置hash值。"""
578+ file_info = parsed_result.file_info
579+ # 如果配置了configurable,需要计算文件的hash值
580+ if file_info and file_info['configurable'] == 'TRUE':
581+ src_target = get_dst_target(file_info, env)
582+ hash_value = make_hash(src_target)
583+ file_info['hash'] = hash_value
584+ return parsed_result
585+ 
586+ 
587+def apply_func(func: Callable[[str], str],
588+ value: Union[List[str], Set[str], str]
589+ ) -> Union[List[str], Set[str], str]:
590+ """对一个字符串,或字符串序列,应用函数。"""
591+ # 如:pkg_softlink列表
592+ if isinstance(value, list):
593+ return list(map(func, value))
594+ # 如:feature集合
595+ if isinstance(value, set):
596+ return set(map(func, value))
597+ return func(value)
598+ 
599+ 
600+REAL_PREFIX = 'real:'
601+ 
602+ 
603+def join_dst_path(base: str, other: str) -> str:
604+ """联结dst_path。"""
605+ if other.startswith('real:'):
606+ other = other[len(REAL_PREFIX):]
607+ return other
608+ return os.path.join(base, other)
609+ 
610+ 
611+def evaluate_info(info: Dict[str, str],
612+ loaded_block: LoadedBlockElement,
613+ env_dict: EnvDict) -> Dict[str, str]:
614+ """info元素求值。"""
615+ dst_keys = ('dst_path',)
616+ 
617+ replace_env_func = partial(replace_env, env_dict)
618+ add_dst_path_func = partial(join_dst_path, loaded_block.dst_path)
619+ 
620+ def upper_value(key: str, value: str) -> Tuple[str, str]:
621+ if key == 'configurable':
622+ return key, value.upper()
623+ return key, value
624+ 
625+ def add_dst_path(key: str, value: str) -> Tuple[str, str]:
626+ if key in dst_keys:
627+ return key, apply_func(add_dst_path_func, value)
628+ return key, value
629+ 
630+ def replace_pkg_inner_softlink(key: str, value: str) -> Tuple[str, str]:
631+ if key == 'pkg_inner_softlink':
632+ return key, 'NA'
633+ return key, value
634+ 
635+ def merge_feature(key: str, value: str) -> Tuple[str, str]:
636+ if key in ('chip', 'feature'):
637+ config_features = config_feature_to_set(value, key)
638+ return key, config_features | getattr(loaded_block, f'{key}s')
639+ return key, value
640+ 
641+ def eval_value(_key: str, value: str) -> str:
642+ if value is not None:
643+ return apply_func(replace_env_func, value)
644+ 
645+ eval_value_func = star_pipe(
646+ upper_value, add_dst_path, replace_pkg_inner_softlink,
647+ merge_feature, eval_value,
648+ )
649+ 
650+ return {
651+ key: eval_value_func(key, value)
652+ for key, value in
653+ itertools.chain(
654+ # 默认值配置
655+ [
656+ ('dst_path', ''), ('configurable', 'FALSE'),
657+ ('chip', None), ('feature', None), ('pkg_feature', None),
658+ ],
659+ info.items()
660+ )
661+ }
662+ 
663+ 
664+def parse_dir_info_elements(loaded_block: LoadedBlockElement,
665+ default_config: Dict[str, str],
666+ package_attr: PackageAttr,
667+ env: ParseEnv) -> List[Dict[str, str]]:
668+ """解析dir_info元素。"""
669+ dir_info_elements: List[ET.Element] = loaded_block.root_ele.findall('dir_info')
670+ dir_infos = []
671+ for item in dir_info_elements:
672+ dir_config = default_config.copy()
673+ dir_config.update(item.attrib)
674+ dir_config['module'] = dir_config.get('value')
675+ for sub_item in list(item):
676+ dir_info = dir_config.copy()
677+ dir_info.update(sub_item.attrib)
678+ dir_info = evaluate_info(dir_info, loaded_block, env.env_dict)
679+ check_value(
680+ dir_info['value'], env.parse_option.package_check, package_attr
681+ )
682+ dir_infos.append(dir_info)
683+ 
684+ return dir_infos
685+ 
686+ 
687+def expand_dir(file_info: FileInfo, get_dst_target_func: Callable[[FileInfo], str]):
688+ """
689+ 如果file_info中配置的路径是文件夹,需要展开到文件
690+ """
691+ file_info_list = []
692+ dir_info_list = []
693+ dst_target = get_dst_target_func(file_info)
694+ 
695+ value_list = file_info.get('value').split('/')
696+ target_name = value_list[-1] if value_list[-1] else value_list[-2]
697+ 
698+ # 这里把当前目录也加入到dir_info_list中
699+ dir_info_copy = file_info.copy()
700+ dir_info_copy['module'] = file_info.get('value')
701+ dir_info_copy['value'] = os.path.join(
702+ file_info.get('install_path', ''), target_name
703+ )
704+ 
705+ # 子目录的权限按照xml中subdir_mod配置,如果没有配置subdir_mod按照install_mod配置
706+ subdir_mod = file_info.get("subdir_mod", None)
707+ if subdir_mod is not None:
708+ dir_info_copy['install_mod'] = subdir_mod
709+ # 被展开的当前目录不需要设置softlink
710+ dir_info_copy['install_softlink'] = 'NA'
711+ dir_info_copy['pkg_inner_softlink'] = 'NA'
712+ dir_info_list.append(dir_info_copy)
713+ 
714+ for root, dirs, files in os.walk(dst_target, followlinks=True):
715+ # 不同操作系统上,os.walk遍历的结果顺序会略有不同,这里按字母排序,保证不同系统一致
716+ dirs.sort()
717+ files.sort()
718+ 
719+ dirs_to_remove = []
720+ for name in dirs:
721+ dirname = os.path.join(root, name)
722+ # 如果是指向目录的软连接,则按照文件处理,无需在安装时创建目录,只需要卸载时删除就行
723+ if os.path.islink(dirname) and not need_dereference(file_info):
724+ copy_file_info = create_file_info(dirname, dst_target, file_info, name, target_name)
725+ # 被展开的子文件不需要设置softlink
726+ copy_file_info['install_softlink'] = 'NA'
727+ copy_file_info['pkg_inner_softlink'] = 'NA'
728+ file_info_list.append(copy_file_info)
729+ dirs_to_remove.append(name)
730+ continue
731+ relative_dirname = os.path.relpath(dirname, dst_target)
732+ dir_info_copy = file_info.copy()
733+ dir_info_copy['module'] = file_info.get('value')
734+ dir_info_copy['value'] = os.path.join(
735+ file_info.get('install_path', ''), target_name, relative_dirname
736+ )
737+ # 被展开的子目录不需要设置softlink
738+ dir_info_copy['install_softlink'] = 'NA'
739+ dir_info_copy['pkg_inner_softlink'] = 'NA'
740+ # 子目录的权限按照xml中subdir_mod配置,如果没有配置subdir_mod按照install_mod配置
741+ subdir_mod = file_info.get("subdir_mod", None)
742+ if subdir_mod is not None:
743+ dir_info_copy['install_mod'] = subdir_mod
744+ dir_info_list.append(dir_info_copy)
745+ for name in files:
746+ filename = os.path.join(root, name)
747+ copy_file_info = create_file_info(filename, dst_target, file_info, name, target_name)
748+ file_info_list.append(copy_file_info)
749+ 
750+ for name in dirs_to_remove:
751+ dirs.remove(name)
752+ return file_info_list, dir_info_list
753+ 
754+ 
755+def create_file_info(dirname, dst_target, file_info, name, target_name):
756+ relative_filename = os.path.relpath(dirname, dst_target)
757+ relative_dir_name = os.path.split(relative_filename)[0]
758+ copy_file_info = file_info.copy()
759+ copy_file_info['value'] = name
760+ copy_file_info['src_path'] = os.path.join(
761+ file_info['src_path'], file_info['value'], relative_dir_name
762+ )
763+ copy_file_info['dst_path'] = os.path.join(
764+ file_info['dst_path'], target_name, relative_dir_name
765+ )
766+ copy_file_info['install_path'] = os.path.join(
767+ file_info.get('install_path', ''), target_name, relative_dir_name
768+ )
769+ return copy_file_info
770+ 
771+ 
772+def expand_file_info_asterisk(parsed_result: FileInfoParsedResult,
773+ env: ParseEnv) -> Iterator[FileInfoParsedResult]:
774+ """展开FileInfoParsedResult中的星号。"""
775+ file_info = parsed_result.file_info
776+ if check_contain_asterisk(file_info.get('value', '')):
777+ dst_prefix = get_dst_prefix(file_info, env)
778+ dst_targets = sorted(glob.glob(get_dst_target(file_info, env)))
779+ if 'exclude' in file_info:
780+ exclude = list(map(methodcaller('strip'), file_info['exclude'].split(';')))
781+ else:
782+ exclude = []
783+ for dst_target in dst_targets:
784+ value = os.path.relpath(dst_target, dst_prefix)
785+ if value in exclude:
786+ continue
787+ new_file_info = file_info.copy()
788+ new_file_info['value'] = value
789+ if 'pkg_inner_softlink' in new_file_info:
790+ # pkg_inner_softlink中的特殊变量$(FILE)替换为展开后的文件名
791+ pkg_inner_softlink = new_file_info['pkg_inner_softlink']
792+ new_file_info['pkg_inner_softlink'] = pkg_inner_softlink.replace(
793+ '$(FILE)', os.path.basename(dst_target)
794+ )
795+ yield parsed_result._replace(file_info=new_file_info)
796+ else:
797+ yield parsed_result
798+ 
799+ 
800+def trans_to_stream(item: Any) -> Iterator[Any]:
801+ """转换为流。"""
802+ yield item
803+ 
804+ 
805+def need_dereference(file_info: FileInfo) -> bool:
806+ """是否需要解引用。"""
807+ if 'dereference' in file_info:
808+ return True
809+ return False
810+ 
811+ 
812+def need_expand(file_info: FileInfo, get_dst_target_func: Callable[[FileInfo], str]) -> bool:
813+ """是否需要展开子目录。"""
814+ if file_info.get('entity') == 'true':
815+ return False
816+ dst_target = get_dst_target_func(file_info)
817+ if os.path.isdir(dst_target):
818+ if need_dereference(file_info):
819+ return True
820+ if os.path.islink(dst_target):
821+ return False
822+ return True
823+ return False
824+ 
825+ 
826+def expand_file_info(parsed_result: FileInfoParsedResult,
827+ use_move: bool,
828+ get_dst_target_func: Callable[[FileInfo], str]) -> FileInfoParsedResult:
829+ """展开FileInfoParsedResult中的目录。"""
830+ file_info = parsed_result.file_info
831+ if need_expand(file_info, get_dst_target_func):
832+ # 如果当前是文件夹,需要展开计算
833+ expand_infos, dir_infos = expand_dir(file_info, get_dst_target_func)
834+ # 实测发现,对于opp包,整体目录cp的安装速度要快于目录中各文件mv
835+ # 可能的原因是,cp遍历目录的速度较快,并且目录中的文件都比较小。mv依赖shell迭代目录中的所有文件。
836+ return FileInfoParsedResult(
837+ merge_dict(file_info, {'is_dir': True}), [], dir_infos, expand_infos
838+ )
839+ 
840+ if use_move:
841+ return FileInfoParsedResult(
842+ {}, [file_info], parsed_result.dir_infos, parsed_result.expand_infos
843+ )
844+ 
845+ return parsed_result
846+ 
847+ 
848+def trans_file_info_to_result(file_info: FileInfo) -> FileInfoParsedResult:
849+ """file_info转换为FileInfoParsedResult。"""
850+ return FileInfoParsedResult(file_info, [], [], [])
851+ 
852+ 
853+def parse_file_element(file_ele: ET.Element,
854+ file_config: Dict[str, str],
855+ loaded_block: LoadedBlockElement,
856+ package_attr: PackageAttr,
857+ env: ParseEnv) -> Iterator[FileInfoParsedResult]:
858+ """解析file元素。"""
859+ file_info = merge_dict(file_config, file_ele.attrib)
860+ file_info = evaluate_info(file_info, loaded_block, env.env_dict)
861+ 
862+ if package_attr.get('expand_asterisk', False):
863+ expand_asterisk_func = partial(expand_file_info_asterisk, env=env)
864+ else:
865+ expand_asterisk_func = trans_to_stream
866+ 
867+ if 'install_path' not in file_info:
868+ file_info['install_path'] = ''
869+ 
870+ trans_file_info_func = pipe(
871+ trans_file_info_to_result,
872+ expand_asterisk_func,
873+ partial(map, partial(config_hash, env=env)),
874+ partial(
875+ map,
876+ partial(
877+ expand_file_info,
878+ use_move=loaded_block.use_move,
879+ get_dst_target_func=partial(get_dst_target, env=env)
880+ )
881+ ),
882+ )
883+ 
884+ yield from trans_file_info_func(file_info)
885+ 
886+ 
887+def parse_file_info_elements(loaded_block: LoadedBlockElement,
888+ default_config: Dict[str, str],
889+ package_attr: PackageAttr,
890+ env: ParseEnv) -> Iterator[FileInfoParsedResult]:
891+ """解析file_info元素。"""
892+ file_info_elements: List[ET.Element] = loaded_block.root_ele.findall('file_info')
893+ for file_info_ele in file_info_elements:
894+ file_config = merge_dict(
895+ default_config,
896+ file_info_ele.attrib,
897+ {'module': file_info_ele.attrib.get('value')}
898+ )
899+ 
900+ for sub_item in list(file_info_ele):
901+ yield from parse_file_element(
902+ sub_item, file_config, loaded_block, package_attr, env
903+ )
904+ 
905+ 
906+def unique_infos(infos: Iterable) -> List[Dict[str, str]]:
907+ """infos去重。"""
908+ cache: Set[str] = set()
909+ new_infos = []
910+ for info in infos:
911+ if info['value'] in cache:
912+ continue
913+ cache.add(info['value'])
914+ new_infos.append(info)
915+ 
916+ return new_infos
917+ 
918+ 
919+def parse_block_config(loaded_block: LoadedBlockElement,
920+ package_attr: PackageAttr,
921+ parse_env: ParseEnv):
922+ """解析块配置。"""
923+ default_config = copy.copy(loaded_block.attrs)
924+ default_config.update(loaded_block.root_ele.attrib)
925+ 
926+ dir_infos = parse_dir_info_elements(
927+ loaded_block,
928+ default_config,
929+ package_attr,
930+ parse_env,
931+ )
932+ file_info_results = list(
933+ chain(
934+ parse_file_info_elements(
935+ loaded_block,
936+ default_config,
937+ package_attr,
938+ parse_env,
939+ )
940+ )
941+ )
942+ 
943+ generate_infos = parse_generate_infos_by_loaded_block(
944+ loaded_block, default_config, parse_env.env_dict
945+ )
946+ 
947+ return BlockConfig(
948+ unique_infos(
949+ itertools.chain(dir_infos,
950+ flatten(result.dir_infos for result in file_info_results)
951+ )
952+ ),
953+ list(flatten(map(attrgetter('move_infos'), file_info_results))),
954+ list(flatten(map(attrgetter('expand_infos'), file_info_results))),
955+ [result.file_info for result in file_info_results if result.file_info],
956+ generate_infos,
957+ )
958+ 
959+ 
960+def make_loaded_block_element(root_ele: ET.Element,
961+ dst_path: str = '') -> LoadedBlockElement:
962+ """创建加载后的块配置。"""
963+ return LoadedBlockElement(root_ele, False, dst_path, set(), set(), {})
964+ 
965+ 
966+def parse_block_element(block_ele: ET.Element,
967+ block_info_attr: Dict[str, str]) -> BlockElement:
968+ """解析单个块配置。"""
969+ 
970+ def filter_attrs(attrs: Dict[str, str]) -> Dict[str, str]:
971+ # block属性中过滤掉dst_path与block_conf_path
972+ # dst_path由单独的参数传递
973+ # block中不需要block_conf_path
974+ return {
975+ key: value
976+ for key, value in attrs.items()
977+ if key not in ('dst_path', 'block_conf_path')
978+ }
979+ 
980+ def with_merged_attrs(attrs: Dict[str, str]) -> BlockElement:
981+ name = attrs.get('name')
982+ block_conf_path = attrs.get('block_conf_path')
983+ 
984+ if not name:
985+ raise BlockConfigError("block's name is not set!")
986+ 
987+ if not block_conf_path:
988+ raise BlockConfigError("block's conf_path is not set!")
989+ 
990+ return BlockElement(
991+ name=name,
992+ block_conf_path=block_conf_path,
993+ dst_path=attrs.get('dst_path', ''),
994+ chips=config_feature_to_set(attrs.get('chip'), 'chip'),
995+ features=config_feature_to_set(attrs.get('feature'), 'feature'),
996+ attrs=filter_attrs(attrs)
997+ )
998+ 
999+ return with_merged_attrs(merge_dict(block_info_attr, block_ele.attrib))
1000+ 
1001+ 
1002+def parse_block_info(block_info: ET.Element) -> List[BlockElement]:
1003+ """解析块配置。"""
1004+ def parse_block_elements(block_elements: List[ET.Element]) -> List[BlockElement]:
1005+ return [
1006+ parse_block_element(block_ele, block_info.attrib) for block_ele in block_elements
1007+ ]
1008+ 
1009+ return parse_block_elements(list(block_info))
1010+ 
1011+ 
1012+def get_block_filepath(block_element: BlockElement) -> str:
1013+ """获取块配置路径。"""
1014+ return os.path.join(
1015+ pkg_utils.TOP_SOURCE_DIR, BLOCK_CONFIG_PATH, block_element.block_conf_path,
1016+ f'{block_element.name}.xml'
1017+ )
1018+ 
1019+ 
1020+def load_block_element(package_attr: PackageAttr,
1021+ block_element: BlockElement) -> LoadedBlockElement:
1022+ """加载块配置。"""
1023+ 
1024+ def with_filepath(block_xml: str):
1025+ if not os.path.exists(block_xml):
1026+ raise BlockConfigError(f"block's config xml {block_xml} does not exist!")
1027+ 
1028+ try:
1029+ return LoadedBlockElement(
1030+ root_ele=ET.parse(block_xml).getroot(),
1031+ use_move=package_attr.get('use_move', False),
1032+ **{
1033+ name: getattr(block_element, name)
1034+ for name in BLOCK_ELEMENT_PASS_THROUGH_ARGS
1035+ }
1036+ )
1037+ except Exception:
1038+ raise BlockConfigError(f"dependent block configuration {block_xml} parse failed!")
1039+ 
1040+ return with_filepath(get_block_filepath(block_element))
1041+ 
1042+ 
1043+def parse_blocks(root_ele: ET.Element,
1044+ package_attr: PackageAttr,
1045+ parse_env: ParseEnv) -> List[BlockConfig]:
1046+ """解析块列表。"""
1047+ return [
1048+ parse_block_config(
1049+ loaded_block, package_attr, parse_env
1050+ )
1051+ for loaded_block in itertools.chain(
1052+ [make_loaded_block_element(root_ele)],
1053+ map(
1054+ partial(load_block_element, package_attr),
1055+ chain.from_iterable(
1056+ map(parse_block_info, root_ele.findall("block_info"))
1057+ )
1058+ )
1059+ )
1060+ ]
1061+ 
1062+ 
1063+def read_version_info(delivery_dir: str, package_attr: PackageAttr) -> Tuple[str, str]:
1064+ if 'install_script' not in package_attr:
1065+ raise InstallScriptNotInPackageError()
1066+ install_script = package_attr['install_script']
1067+ install_script_paths = install_script.split('/')
1068+ if len(install_script_paths) < 2:
1069+ raise InstallScriptFormatError()
1070+ 
1071+ version_path = os.path.join(delivery_dir, *install_script_paths[:-2], "version.info")
1072+ if not os.path.isfile(version_path):
1073+ raise VersionInfoNotExist(version_path)
1074+
1075+ with open(version_path, 'r') as file:
1076+ line1 = file.readline().strip()
1077+ line2 = file.readline().strip()
1078+ version = line1.split("=")[1]
1079+ version_dir = line2.split("=")[1]
1080+ m = re.match(r'[.a-zA-Z0-9]+$', version) or re.match(r'[-a-zA-Z.0-9]+$', version)
1081+ if not m:
1082+ raise VersionFormatNotMatch()
1083+
1084+ return version, version_dir
1085+ 
1086+ 
1087+def parse_xml_config(filepath: str,
1088+ delivery_dir: str,
1089+ parse_option: ParseOption,
1090+ args: Namespace) -> XmlConfig:
1091+ """解析打包xml配置。"""
1092+ try:
1093+ tree = ET.parse(filepath)
1094+ xml_root = tree.getroot()
1095+ except ET.ParseError as ex:
1096+ CommLog.cilog_error("xml parse %s failed: %s!", filepath, ex)
1097+ sys.exit(FAIL)
1098+ 
1099+ default_config = xml_root.attrib.copy()
1100+ 
1101+ package_attr = parse_package_attr(xml_root, args)
1102+ try:
1103+ version, version_dir = read_version_info(delivery_dir, package_attr)
1104+ except InstallScriptNotInPackageError:
1105+ CommLog.cilog_error("The install_script is not configured in the package_info in %s!", filepath)
1106+ sys.exit(FAIL)
1107+ except InstallScriptFormatError:
1108+ CommLog.cilog_error("The install_script format is illegal in %s! More directory levels are needed.", filepath)
1109+ sys.exit(FAIL)
1110+ except VersionInfoNotExist as ex:
1111+ CommLog.cilog_error("The version.info file %s does not exist in %s!", str(ex), filepath)
1112+ sys.exit(FAIL)
1113+ if args.disable_multi_version:
1114+ version_dir = None
1115+ timestamp = get_timestamp(args)
1116+ try:
1117+ env_dict = parse_env_dict(
1118+ parse_option.os_arch, package_attr, version, version_dir, timestamp
1119+ )
1120+ except ParseOsArchError:
1121+ CommLog.cilog_error(
1122+ "os_arch %s is not correctly configured: %s!",
1123+ parse_option.os_arch, filepath
1124+ )
1125+ sys.exit(FAIL)
1126+ 
1127+ parse_env = ParseEnv(
1128+ env_dict, parse_option, delivery_dir, pkg_utils.TOP_SOURCE_DIR
1129+ )
1130+ 
1131+ blocks = parse_blocks(
1132+ xml_root, package_attr, parse_env
1133+ )
1134+ 
1135+ if is_multi_version(version_dir):
1136+ fill_is_common_path_func = partial(
1137+ fill_is_common_path, target_env=env_dict.get('TARGET_ENV')
1138+ )
1139+ else:
1140+ fill_is_common_path_func = iter
1141+ 
1142+ return XmlConfig(
1143+ default_config, package_attr, None, blocks, version, None,
1144+ PackerConfig(fill_is_common_path_func)
1145+ )
Ascripts/package/common/py/utils/comm_log.py+63-0
@@ -0,0 +1,63 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
12+ 
13+import time
14+import inspect
15+import logging
16+ 
17+logging.basicConfig(format='[%(asctime)s] [%(levelname)s] [%(pathname)s] [line:%(lineno)d] %(message)s',
18+ level=logging.INFO)
19+ 
20+ 
21+class CommLog:
22+ @staticmethod
23+ def cilog_get_timestamp():
24+ return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())
25+ 
26+ @staticmethod
27+ def cilog_print_element(cilog_element):
28+ print("["+cilog_element+"]", end=' ')
29+ return
30+ 
31+ @staticmethod
32+ def cilog_logmsg(log_level, filename, line_no, log_msg, *log_paras):
33+ log_timestamp = CommLog.cilog_get_timestamp()
34+ CommLog.cilog_print_element(log_timestamp)
35+ CommLog.cilog_print_element(log_level)
36+ CommLog.cilog_print_element(filename)
37+ CommLog.cilog_print_element(str(line_no))
38+ print(log_msg % log_paras[0])
39+ return
40+ 
41+ @staticmethod
42+ def cilog_error(log_msg, *log_paras):
43+ frame = inspect.currentframe().f_back
44+ line_no = frame.f_lineno
45+ filename = frame.f_code.co_filename
46+ CommLog.cilog_logmsg("ERROR", filename, line_no, log_msg, log_paras)
47+ return
48+ 
49+ @staticmethod
50+ def cilog_warning(log_msg, *log_paras):
51+ frame = inspect.currentframe().f_back
52+ line_no = frame.f_lineno
53+ filename = frame.f_code.co_filename
54+ CommLog.cilog_logmsg("WARNING", filename, line_no, log_msg, log_paras)
55+ return
56+ 
57+ @staticmethod
58+ def cilog_info(log_msg, *log_paras):
59+ frame = inspect.currentframe().f_back
60+ line_no = frame.f_lineno
61+ filename = frame.f_code.co_filename
62+ CommLog.cilog_logmsg("INFO", filename, line_no, log_msg, log_paras)
63+ return
Ascripts/package/common/py/utils/funcbase.py+89-0
@@ -0,0 +1,89 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
12+ 
13+"""函数基础库。"""
14+ 
15+import operator
16+from typing import Callable, Iterator, TypeVar
17+ 
18+ 
19+A = TypeVar('A')
20+ 
21+ 
22+def constant(value: A) -> Callable[..., A]:
23+ """常量值。"""
24+ def constant_inner(*_args, **_kwargs) -> A:
25+ return value
26+ 
27+ return constant_inner
28+ 
29+ 
30+def dispatch(*funcs):
31+ """分派应用。"""
32+ def dispatch_inner(*args, **kwargs) -> Iterator:
33+ return (func(*args, **kwargs) for func in funcs)
34+ 
35+ return dispatch_inner
36+ 
37+ 
38+def pipe(*funcs):
39+ """串联多个函数。"""
40+ def pipe_func(*args, **k_args):
41+ result = funcs[0](*args, **k_args)
42+ for func in funcs[1:]:
43+ result = func(result)
44+ return result
45+ 
46+ return pipe_func
47+ 
48+ 
49+def identity(value: A) -> A:
50+ """同一。"""
51+ return value
52+ 
53+ 
54+def invoke(func, *args, **kwargs):
55+ """调用。"""
56+ return func(*args, **kwargs)
57+ 
58+ 
59+def side_effect(*funcs):
60+ """调用函数,产生副作用,但不影响管道结果。"""
61+ def side_effect_func(arg):
62+ for func in funcs:
63+ # 不保留结果
64+ func(arg)
65+ return arg
66+ 
67+ return side_effect_func
68+ 
69+ 
70+def star_apply(func):
71+ """列表展开再应用。"""
72+ def star_apply_func(arg):
73+ return func(*arg)
74+ 
75+ return star_apply_func
76+ 
77+ 
78+def any_(*funcs) -> Callable:
79+ """高阶any。
80+ 注意,any有短路效果。"""
81+ return pipe(
82+ dispatch(*funcs),
83+ any,
84+ )
85+ 
86+ 
87+def not_(func) -> Callable:
88+ """高阶not。"""
89+ return pipe(func, operator.not_)
Ascripts/package/common/py/utils/pkg_utils.py+198-0
@@ -0,0 +1,198 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
12+ 
13+"""基础构件。"""
14+ 
15+import os
16+from functools import partial
17+from itertools import chain, tee
18+from operator import methodcaller
19+from pathlib import Path
20+from typing import Callable, Dict, Iterator, List, Optional, TypeVar, Set
21+ 
22+TOP_DIR = str(Path(__file__).resolve().parents[5])
23+TOP_SOURCE_DIR = TOP_DIR + '/scripts/'
24+DELIVERY_PATH = "build/_CPack_Packages/makeself_staging"
25+CONFIG_SCRIPT_PATH = 'package'
26+BLOCK_CONFIG_PATH = 'package/module'
27+ 
28+SUCCESS = 0
29+FAIL = -1
30+ 
31+ 
32+A = TypeVar('A')
33+ 
34+ 
35+class PackageError(Exception):
36+ """打包异常基类。"""
37+ 
38+ 
39+class PackageConfigError(PackageError):
40+ """打包配置错误异常。"""
41+ 
42+ 
43+class BlockConfigError(PackageError):
44+ """块配置错误异常。"""
45+ 
46+ 
47+class ParseOsArchError(PackageError):
48+ """解析os_arch失败异常。"""
49+ 
50+ 
51+class EnvNotSupported(PackageError):
52+ """环境变量不支持异常。"""
53+ 
54+ 
55+class ContainAsteriskError(PackageError):
56+ """包含星号异常。"""
57+ 
58+ def __init__(self, value: str):
59+ super().__init__()
60+ self.value = value
61+ 
62+ 
63+class FilelistError(PackageError):
64+ """文件列表异常。"""
65+ 
66+ 
67+class UnknownOperateTypeError(PackageError):
68+ """未知的操作类型。"""
69+ 
70+ 
71+class PackageNameEmptyError(PackageError):
72+ """包名为空错误。"""
73+ 
74+ 
75+class GenerateFilelistError(PackageError):
76+ """生成文件列表文件异常。"""
77+ 
78+ def __init__(self, filename: str):
79+ super().__init__()
80+ self.filename = filename
81+ 
82+ 
83+class IllegalVersionDir(PackageError):
84+ """version_dir配置错误。"""
85+ 
86+ 
87+class CompressError(PackageError):
88+ """打包错误。"""
89+ 
90+ def __init__(self, package_name: Optional[str]):
91+ super().__init__(package_name)
92+ self.package_name = package_name
93+ 
94+ 
95+class InstallScriptNotInPackageError(PackageError):
96+ """package_info中没有配置install_script。"""
97+ 
98+ 
99+class InstallScriptFormatError(PackageError):
100+ """install_script配置格式错误。"""
101+ 
102+ def __init__(self, detail: str):
103+ super().__init__(detail)
104+ self.detail = detail
105+ 
106+ 
107+class VersionInfoNotExist(PackageError):
108+ """version.info文件不存在。"""
109+ 
110+ def __init__(self, filepath: str):
111+ super().__init__(f"Version info file not found: {filepath}")
112+ self.filepath = filepath
113+ 
114+ 
115+def flatten(list_of_lists):
116+ """Flatten one level of nesting"""
117+ return chain.from_iterable(list_of_lists)
118+ 
119+ 
120+def merge_dict(base: Dict, *news: Dict):
121+ """合并两个字典。"""
122+ result = base.copy()
123+ for new in news:
124+ result.update(new)
125+ return result
126+ 
127+ 
128+def star_pipe(*funcs):
129+ """串联多个函数。解包结果。"""
130+ def pipe_func(*args, **k_args):
131+ result = funcs[0](*args, **k_args)
132+ for func in funcs[1:]:
133+ # 解包元组或列表结果
134+ result = func(*result)
135+ return result
136+ 
137+ return pipe_func
138+ 
139+ 
140+def swap_args(func):
141+ """交换函数前两个参数。"""
142+ def inner(fst, snd, *args, **k_args):
143+ return func(snd, fst, *args, **k_args)
144+ return inner
145+ 
146+ 
147+def conditional_apply(predicate, func):
148+ """条件下应用函数。"""
149+ def conditional_apply_func(arg):
150+ if predicate(arg):
151+ return func(arg)
152+ return arg
153+ 
154+ return conditional_apply_func
155+ 
156+ 
157+def pairwise(iterable):
158+ """s -> (s0,s1), (s1,s2), (s2, s3), ..."""
159+ a, b = tee(iterable)
160+ next(b, None)
161+ return zip(a, b)
162+ 
163+ 
164+def path_join(base: Optional, *others: str) -> Optional:
165+ """路径联合。"""
166+ if base is None:
167+ return None
168+ return os.path.join(base, *others)
169+ 
170+ 
171+def yield_if(data, predicate: Callable) -> Iterator:
172+ """条件满足则产生。"""
173+ if predicate(data):
174+ yield data
175+ 
176+ 
177+def config_feature_to_set(feature_str: str, feature_type: str = 'feature') -> Set[str]:
178+ """配置feature转换为集合。"""
179+ if feature_str is None:
180+ return set()
181+ 
182+ if isinstance(feature_str, set):
183+ return feature_str
184+ 
185+ if feature_str == '':
186+ raise PackageConfigError(f"Not allow to config {feature_type} empty.")
187+ 
188+ features = set(feature_str.split(';'))
189+ if 'all' in features:
190+ raise PackageConfigError(f"Not allow to config {feature_type} all.")
191+ return features
192+ 
193+ 
194+def config_feature_to_string(features: Set[str]) -> str:
195+ """配置feature集合转换为字符串。"""
196+ if not features:
197+ return 'all'
198+ return ';'.join(sorted(features))
Ascripts/package/common/py/version_info.py+432-0
@@ -0,0 +1,432 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
12+ 
13+import os
14+import re
15+import sys
16+import xml.etree.ElementTree as ET
17+from functools import total_ordering
18+from pathlib import Path
19+from typing import Dict, List, NamedTuple, Optional, Tuple, Union
20+from .utils.comm_log import CommLog
21+ 
22+ 
23+class VersionInfoError(Exception):
24+ """版本信息异常基类。"""
25+ 
26+ 
27+class VersionFormatNotMatch(VersionInfoError):
28+ """版本格式未匹配。"""
29+ 
30+ 
31+class IntervalFormatNotMatch(VersionInfoError):
32+ """区间格式未匹配。"""
33+ 
34+ 
35+class DuplicatedPkgConfig(VersionInfoError):
36+ """解析版本配置失败。重复的包配置。"""
37+ 
38+ def __init__(self, pkg_name):
39+ super().__init__(pkg_name)
40+ self.pkg_name = pkg_name
41+ 
42+ 
43+class ParseVersionFailed(VersionInfoError):
44+ """解析版本失败。"""
45+ 
46+ 
47+class CollectRequiresFailed(VersionInfoError):
48+ """收集包需求失败。"""
49+ 
50+ def __init__(self, pkg_name, version_str, msg):
51+ super().__init__(pkg_name, version_str, msg)
52+ self.pkg_name = pkg_name
53+ self.version_str = version_str
54+ self.msg = msg
55+ 
56+ 
57+@total_ordering
58+class Version:
59+ """版本号。"""
60+ 
61+ def __init__(self, version):
62+ self.version = version
63+ 
64+ @classmethod
65+ def match(cls, input_str):
66+ """输入字符串是否匹配版本号模式。"""
67+ m = re.match(r'[.a-zA-Z0-9]+$', input_str)
68+ return bool(m)
69+ 
70+ @classmethod
71+ def parse(cls, input_str):
72+ """解析版本号。"""
73+ if not cls.match(input_str):
74+ raise VersionFormatNotMatch()
75+ 
76+ return cls(input_str)
77+ 
78+ @classmethod
79+ def try_convert_to_int_list(cls, str_list):
80+ """尝试转换为int数组。"""
81+ for idx, item in enumerate(str_list):
82+ try:
83+ int_item = int(item)
84+ str_list[idx] = int_item
85+ except ValueError:
86+ pass
87+ 
88+ def to_required_list(self):
89+ """转换为版本需求字符串列表。"""
90+ return [self.version]
91+ 
92+ def __eq__(self, other):
93+ """等于。"""
94+ if not isinstance(other, self.__class__):
95+ return False
96+ return self.version == other.version
97+ 
98+ def __lt__(self, other):
99+ """小于。"""
100+ if not isinstance(other, self.__class__):
101+ return True
102+ 
103+ self_list = self.version.split('.')
104+ other_list = other.version.split('.')
105+ 
106+ self.try_convert_to_int_list(self_list)
107+ self.try_convert_to_int_list(other_list)
108+ 
109+ self_tuple = tuple(self_list)
110+ other_tuple = tuple(other_list)
111+ 
112+ return self_tuple < other_tuple
113+ 
114+ def __str__(self):
115+ return self.version
116+ 
117+ def __repr__(self):
118+ return repr(self.version)
119+ 
120+ 
121+class Point(NamedTuple):
122+ """区间端点。"""
123+ type_: int # 类型,0为闭区间,1为开区间
124+ value: Version
125+ 
126+ 
127+class Interval(NamedTuple):
128+ """版本号区间。"""
129+ low: Point
130+ high: Point
131+ 
132+ @classmethod
133+ def match(cls, input_str: str) -> bool:
134+ """输入字符串是否匹配区间模式。"""
135+ if not input_str.startswith('(') and not input_str.startswith('['):
136+ return False
137+ if not input_str.endswith(')') and not input_str.endswith(']'):
138+ return False
139+ input_str = input_str[1:-1]
140+ if input_str.count(',') > 1:
141+ return False
142+ return True
143+ 
144+ @classmethod
145+ def parse(cls, input_str):
146+ """解析版本号区间。"""
147+ if not cls.match(input_str):
148+ raise IntervalFormatNotMatch()
149+ 
150+ if input_str[0] == '[':
151+ low_type = 0
152+ elif input_str[0] == '(':
153+ low_type = 1
154+ else:
155+ assert False, 'should not go here.'
156+ 
157+ if input_str[-1] == ']':
158+ high_type = 0
159+ elif input_str[-1] == ')':
160+ high_type = 1
161+ else:
162+ assert False, 'should not go here.'
163+ 
164+ input_str = input_str[1:-1]
165+ input_list = input_str.split(',')
166+ low = input_list[0].strip()
167+ if len(input_list) > 1:
168+ high = input_list[1].strip()
169+ else:
170+ high = None
171+ 
172+ if low:
173+ low_version = Point(low_type, Version(low))
174+ else:
175+ low_version = None
176+ 
177+ if high:
178+ high_version = Point(high_type, Version(high))
179+ else:
180+ high_version = None
181+ 
182+ return cls(low=low_version, high=high_version)
183+ 
184+ def to_required_list(self):
185+ """转换为版本需求字符串列表。"""
186+ result = []
187+ 
188+ if self.low:
189+ if self.low.type_ == 0:
190+ operator = '>='
191+ else:
192+ operator = '>'
193+ required_str = '{0}{1}'.format(operator, self.low.value.version)
194+ result.append(required_str)
195+ 
196+ if self.high:
197+ if self.high.type_ == 0:
198+ operator = '<='
199+ else:
200+ operator = '<'
201+ required_str = '{0}{1}'.format(operator, self.high.value.version)
202+ result.append(required_str)
203+ 
204+ return result
205+ 
206+ 
207+class Require(NamedTuple):
208+ """包需求。"""
209+ pkg_name: str
210+ versions: List
211+ 
212+ @classmethod
213+ def _sort_key(cls, item) -> Tuple:
214+ """排序键。"""
215+ if isinstance(item, Interval):
216+ # 如果存在区间左值,则左值参与排序。
217+ if item.low:
218+ return item.low.value, item.low.type_
219+ # 否则使用区间右值,由于开区间更小,所以type_取负。
220+ return item.high.value, -item.high.type_
221+ 
222+ return item, 0
223+ 
224+ @classmethod
225+ def _sort_versions(cls, versions: List) -> bool:
226+ """排序版本序列。"""
227+ versions.sort(key=cls._sort_key)
228+ return True
229+ 
230+ @classmethod
231+ def _to_required_list(cls, versions: List) -> List[str]:
232+ """转换为版本需求字符串列表。"""
233+ result = []
234+ for version in versions:
235+ requires = version.to_required_list()
236+ result.extend(requires)
237+ 
238+ return result
239+ 
240+ @classmethod
241+ def _to_required_str(cls, versions: List) -> str:
242+ """转换为版本需求字符串。"""
243+ requires = cls._to_required_list(versions)
244+ required_str = ', '.join(requires)
245+ 
246+ return required_str
247+ 
248+ def sort_versions(self) -> bool:
249+ """排序版本序列。"""
250+ return self._sort_versions(self.versions)
251+ 
252+ def to_required_full_str(self) -> str:
253+ """转换为版本需求字符串。"""
254+ required_str = self._to_required_str(self.versions)
255+ required_full_str = 'required_package_{0}_version="{1}"'.format(self.pkg_name, required_str)
256+ return required_full_str
257+ 
258+ 
259+class ItemElement(NamedTuple):
260+ """item元素。"""
261+ name: str
262+ version: str
263+ 
264+ @classmethod
265+ def parse(cls, item_ele: ET.Element, cur_ver: str):
266+ """解析item元素。"""
267+ name = item_ele.attrib['name']
268+ version = item_ele.attrib['version'].replace("$(CUR_VER)", cur_ver)
269+ return cls(name=name, version=version)
270+ 
271+ @classmethod
272+ def skip(cls, item_ele: ET.Element):
273+ """是否跳过item元素。"""
274+ version = item_ele.attrib['version']
275+ return version.strip() == ''
276+ 
277+ 
278+class CompatibleElement(NamedTuple):
279+ """compatible元素。"""
280+ items: List
281+ 
282+ @classmethod
283+ def parse(cls, compatible_ele: ET.Element, cur_ver: str):
284+ """解析compatible元素。"""
285+ items = []
286+ for item_ele in compatible_ele.findall("./item"):
287+ if ItemElement.skip(item_ele):
288+ continue
289+ item = ItemElement.parse(item_ele, cur_ver)
290+ items.append(item)
291+ return cls(items=items)
292+ 
293+ 
294+def is_version_number(version: str) -> bool:
295+ """字符串是否为版本号。"""
296+ has_slash = '/' in version
297+ return not has_slash and len(version.split(".")) >= 3
298+ 
299+ 
300+class VersionXml(NamedTuple):
301+ """版本配置。"""
302+ release_version: str
303+ version_dir: str
304+ packages: Dict
305+ 
306+ @classmethod
307+ def match(cls, filepath: Union[Path, str]) -> bool:
308+ """文件路径是否匹配版本信息文件。"""
309+ return str(filepath).endswith('.xml')
310+ 
311+ @classmethod
312+ def parse_version(cls, version_str: str):
313+ """解析版本配置。"""
314+ ret = Interval.match(version_str)
315+ if ret:
316+ result = Interval.parse(version_str)
317+ return result
318+ 
319+ ret = Version.match(version_str)
320+ if ret:
321+ result = Version.parse(version_str)
322+ return result
323+ 
324+ raise ParseVersionFailed()
325+ 
326+ def get_release_version(self):
327+ """获取发布版本号。"""
328+ return self.release_version
329+ 
330+ def get_version_dir(self):
331+ """获取多版本目录。"""
332+ return self.version_dir
333+ 
334+ def collect_requires(self, package: str) -> List[Require]:
335+ """收集对应包的包需求列表。"""
336+ requires = {}
337+ 
338+ if package not in self.packages:
339+ return []
340+ 
341+ compatible = self.packages[package]
342+ 
343+ for item in compatible.items:
344+ pkg_name = item.name
345+ if pkg_name not in requires:
346+ requires[pkg_name] = Require(pkg_name=pkg_name, versions=[])
347+ 
348+ version_str = item.version
349+ try:
350+ version = self.parse_version(version_str)
351+ except ParseVersionFailed as ex:
352+ msg = f'parse pkg {pkg_name} version {version_str} failed'
353+ raise CollectRequiresFailed(pkg_name, version_str, msg) from ex
354+ 
355+ requires[pkg_name].versions.append(version)
356+ 
357+ result = []
358+ for pkg_name in sorted(requires.keys()):
359+ requires[pkg_name].sort_versions()
360+ result.append(requires[pkg_name])
361+ 
362+ return result
363+ 
364+ 
365+def get_version_dir(version_xml: Optional[VersionXml],
366+ disable_multi_version: bool,
367+ version_dir: Optional[str]) -> Optional[str]:
368+ """获取版本目录名。"""
369+ if disable_multi_version:
370+ return None
371+ 
372+ if version_dir:
373+ return version_dir
374+ 
375+ # 支持从version.xml中获取version_dir
376+ if version_xml and version_xml.get_version_dir():
377+ return version_xml.get_version_dir()
378+ 
379+ return None
380+ 
381+ 
382+def is_multi_version(version_dir: str) -> bool:
383+ """是否多版本。"""
384+ return bool(version_dir)
385+ 
386+ 
387+class VersionInfo(NamedTuple):
388+ """版本信息。"""
389+ install_version_info: bool
390+ install_version_info_attrib: Optional[Dict[str, str]]
391+ itf_versions: List[str]
392+ version: str
393+ version_xml: Optional[VersionXml]
394+ timestamp: Optional[str]
395+ 
396+ 
397+class VersionInfoFile(NamedTuple):
398+ """生成的版本配置。"""
399+ version: str
400+ itf_version_info: Optional[str] = None
401+ requires: Optional[List[Require]] = None
402+ version_dir: Optional[str] = None
403+ timestamp: Optional[str] = None
404+ 
405+ def _get_content(self) -> str:
406+ """获取版本配置内容。"""
407+ lines = ['Version={0}'.format(self.version)]
408+ if self.version_dir:
409+ lines.append('version_dir={0}'.format(self.version_dir))
410+ if self.timestamp:
411+ lines.append('timestamp={0}'.format(self.timestamp))
412+ if self.itf_version_info:
413+ lines.append(self.itf_version_info)
414+ 
415+ if self.requires:
416+ requires_str = [require.to_required_full_str() for require in self.requires]
417+ lines.extend(requires_str)
418+ 
419+ lines.append('')
420+ 
421+ return '\n'.join(lines)
422+ 
423+ def save(self, target_path: Union[Path, str]):
424+ """保存版本配置。"""
425+ content = self._get_content()
426+ 
427+ target_dir = os.path.dirname(target_path)
428+ if not os.path.exists(target_dir):
429+ os.makedirs(target_dir)
430+ 
431+ with open(target_path, 'w') as file:
432+ file.write(content)
Ascripts/package/common/sh/check_version_required.awk+281-0
@@ -0,0 +1,281 @@
1+# ----------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# ----------------------------------------------------------------------------
10+ 
11+function strip(input) {
12+ sub("^ +", "", input)
13+ sub(" +$", "", input)
14+ return input
15+}
16+ 
17+function check_compatible_le(version_arr, len_version_arr, require_arr, len_require_arr, i) {
18+ for (i = 1; i <= len_require_arr; i++) {
19+ if (require_arr[i] == "") {
20+ continue
21+ }
22+ # len_version_arr lt len_require_arr
23+ if (i > len_version_arr) {
24+ return 1
25+ }
26+ if (version_arr[i] < require_arr[i]) {
27+ return 1
28+ }
29+ if (version_arr[i] > require_arr[i]) {
30+ return 0
31+ }
32+ }
33+ if (len_version_arr > len_require_arr) {
34+ return 1
35+ }
36+ # len_version_arr eq len_require_arr
37+ return 1
38+}
39+ 
40+function check_compatible_lt(version_arr, len_version_arr, require_arr, len_require_arr, i) {
41+ for (i = 1; i <= len_require_arr; i++) {
42+ if (require_arr[i] == "") {
43+ continue
44+ }
45+ # len_version_arr lt len_require_arr
46+ if (i > len_version_arr) {
47+ return 1
48+ }
49+ if (version_arr[i] < require_arr[i]) {
50+ return 1
51+ }
52+ if (version_arr[i] > require_arr[i]) {
53+ return 0
54+ }
55+ }
56+ if (len_version_arr > len_require_arr) {
57+ return 0
58+ }
59+ # len_version_arr eq len_require_arr
60+ return 0
61+}
62+ 
63+function check_compatible_ge(version_arr, len_version_arr, require_arr, len_require_arr, i) {
64+ for (i = 1; i <= len_require_arr; i++) {
65+ if (require_arr[i] == "") {
66+ continue
67+ }
68+ # len_version_arr lt len_require_arr
69+ if (i > len_version_arr) {
70+ return 0
71+ }
72+ if (version_arr[i] < require_arr[i]) {
73+ return 0
74+ }
75+ if (version_arr[i] > require_arr[i]) {
76+ return 1
77+ }
78+ }
79+ if (len_version_arr > len_require_arr) {
80+ return 1
81+ }
82+ # len_version_arr eq len_require_arr
83+ return 1
84+}
85+ 
86+function check_compatible_gt(version_arr, len_version_arr, require_arr, len_require_arr, i) {
87+ for (i = 1; i <= len_require_arr; i++) {
88+ if (require_arr[i] == "") {
89+ continue
90+ }
91+ # len_version_arr lt len_require_arr
92+ if (i > len_version_arr) {
93+ return 0
94+ }
95+ if (version_arr[i] < require_arr[i]) {
96+ return 0
97+ }
98+ if (version_arr[i] > require_arr[i]) {
99+ return 1
100+ }
101+ }
102+ if (len_version_arr > len_require_arr) {
103+ return 1
104+ }
105+ # len_version_arr eq len_require_arr
106+ return 0
107+}
108+ 
109+function check_compatible_eq(version_arr, len_version_arr, require_arr, len_require_arr, i) {
110+ for (i = 1; i <= len_require_arr; i++) {
111+ if (require_arr[i] == "") {
112+ continue
113+ }
114+ # len_version_arr lt len_require_arr
115+ if (i > len_version_arr) {
116+ return 0
117+ }
118+ if (version_arr[i] != require_arr[i]) {
119+ return 0
120+ }
121+ }
122+ if (len_version_arr > len_require_arr) {
123+ return 1
124+ }
125+ # len_version_arr eq len_require_arr
126+ return 1
127+}
128+ 
129+function check_compatible(version_arr, len_version_arr, require, require_arr, len_require_arr, pos) {
130+ len_require_arr = split(require, require_arr, ".")
131+ 
132+ pos = match(require_arr[1], /^>=/)
133+ if (pos != 0) {
134+ require_arr[1] = substr(require_arr[1], pos + RLENGTH)
135+ return check_compatible_ge(version_arr, len_version_arr, require_arr, len_require_arr)
136+ }
137+ 
138+ pos = match(require_arr[1], /^>/)
139+ if (pos != 0) {
140+ require_arr[1] = substr(require_arr[1], pos + RLENGTH)
141+ return check_compatible_gt(version_arr, len_version_arr, require_arr, len_require_arr)
142+ }
143+ 
144+ pos = match(require_arr[1], /^<=/)
145+ if (pos != 0) {
146+ require_arr[1] = substr(require_arr[1], pos + RLENGTH)
147+ return check_compatible_le(version_arr, len_version_arr, require_arr, len_require_arr)
148+ }
149+ 
150+ pos = match(require_arr[1], /^</)
151+ if (pos != 0) {
152+ require_arr[1] = substr(require_arr[1], pos + RLENGTH)
153+ return check_compatible_lt(version_arr, len_version_arr, require_arr, len_require_arr)
154+ }
155+ 
156+ return check_compatible_eq(version_arr, len_version_arr, require_arr, len_require_arr)
157+}
158+ 
159+BEGIN {
160+ len_all_required_arr = split(all_required, all_required_arr, ",")
161+ 
162+ compated = 0
163+ 
164+ in_gt = 0
165+ matched_gt = 0
166+ 
167+ len_version_arr = split(version, version_arr, ".")
168+ 
169+ for (i = 1; i <= len_all_required_arr; i++) {
170+ all_required_arr[i] = strip(all_required_arr[i])
171+ one_compated = check_compatible(version_arr, len_version_arr, all_required_arr[i])
172+ 
173+ pos = match(all_required_arr[i], /^>/)
174+ if (pos != 0) {
175+ gt_require = 1
176+ lt_require = 0
177+ eq_require = 0
178+ } else {
179+ pos = match(all_required_arr[i], /^</)
180+ if (pos != 0) {
181+ gt_require = 0
182+ lt_require = 1
183+ eq_require = 0
184+ } else {
185+ gt_require = 0
186+ lt_require = 0
187+ eq_require = 1
188+ }
189+ }
190+ 
191+ if (matched_gt) {
192+ if (one_compated) {
193+ # gt after gt, all compated.
194+ if (gt_require) {
195+ matched_gt = 1
196+ in_gt = 1
197+ continue
198+ }
199+ # lt after gt, all compated.
200+ if (lt_require) {
201+ compated = 1
202+ matched_gt = 0
203+ in_gt = 0
204+ break
205+ }
206+ # eq after gt, all compated.
207+ if (eq_require) {
208+ # eq compated, go.
209+ compated = 1
210+ break
211+ }
212+ } else {
213+ # miscompated.
214+ # gt after gt, compated first gt. miscompated second gt.
215+ if (gt_require) {
216+ matched_gt = 0
217+ in_gt = 1
218+ continue
219+ }
220+ # lt after gt, compated first gt. miscompated second lt.
221+ if (lt_require) {
222+ matched_gt = 0
223+ in_gt = 0
224+ continue
225+ }
226+ # eq after gt, compated first gt. miscompated second eq.
227+ if (eq_require) {
228+ continue
229+ }
230+ }
231+ } else {
232+ if (one_compated) {
233+ if (gt_require) {
234+ matched_gt = 1
235+ in_gt = 1
236+ continue
237+ }
238+ if (lt_require) {
239+ if (in_gt) {
240+ matched_gt = 0
241+ in_gt = 0
242+ continue
243+ } else {
244+ compated = 1
245+ break
246+ }
247+ }
248+ if (eq_require) {
249+ # eq compated, go.
250+ compated = 1
251+ break
252+ }
253+ } else {
254+ # miscompated.
255+ if (gt_require) {
256+ matched_gt = 0
257+ in_gt = 1
258+ continue
259+ }
260+ if (lt_require) {
261+ matched_gt = 0
262+ in_gt = 0
263+ continue
264+ }
265+ if (eq_require) {
266+ continue
267+ }
268+ }
269+ }
270+ }
271+ 
272+ if (matched_gt) {
273+ compated = 1
274+ }
275+ 
276+ if (compated == 0) {
277+ printf("F")
278+ } else {
279+ printf("T")
280+ }
281+}
Ascripts/package/common/sh/cleanup.sh+14-0
@@ -0,0 +1,14 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+set -e
13+ 
14+rm -rf $(pwd)
Ascripts/package/common/sh/common_func.inc+1236-0
@@ -0,0 +1,1236 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+## module log
13+ 
14+if [ "$(id -u)" != "0" ]; then
15+ COMM_LOG_DIR="${HOME}/var/log/ascend_seclog"
16+else
17+ COMM_LOG_DIR="/var/log/ascend_seclog"
18+fi
19+ 
20+COMM_OPERATION_LOGFILE="${COMM_LOG_DIR}/operation.log"
21+COMM_LOGFILE="${COMM_LOG_DIR}/ascend_install.log"
22+COMM_USERNAME="$(id -un)"
23+COMM_USERGROUP="$(id -gn)"
24+ 
25+LOG_PKG_NAME="Common"
26+LOG_FILE="/dev/null"
27+LOG_STYLE="normal"
28+ 
29+# 初始化日志系统
30+comm_init_log() {
31+ if [ ! -d "$COMM_LOG_DIR" ]; then
32+ mkdir -p "$COMM_LOG_DIR"
33+ fi
34+ if [ $(id -u) -ne 0 ]; then
35+ chmod 740 "$COMM_LOG_DIR"
36+ else
37+ chmod 750 "$COMM_LOG_DIR"
38+ fi
39+ if [ ! -f "$COMM_LOGFILE" ]; then
40+ touch "$COMM_LOGFILE"
41+ fi
42+ chmod 640 "$COMM_LOGFILE"
43+}
44+ 
45+# 组合格式化后的日志信息
46+# _outvar : [输出变量] 格式化后的日志信息
47+# _log_type : 日志级别
48+# _msg : 日志信息
49+_comm_compose_log_msg() {
50+ local _outvar="$1"
51+ local _log_type="$2"
52+ local _msg="$3"
53+ local _cur_date="$(date +'%Y-%m-%d %H:%M:%S')"
54+ local _result
55+ 
56+ if [ "${LOG_STYLE}" = "no-colon" ]; then
57+ _result="[${LOG_PKG_NAME}] [${_cur_date}] [${_log_type}]${_msg}"
58+ else
59+ _result="[${LOG_PKG_NAME}] [${_cur_date}] [${_log_type}]: ${_msg}"
60+ fi
61+ eval "${_outvar}=\"${_result}\""
62+}
63+ 
64+# 输出格式化后的消息
65+_comm_echo_log_msg() {
66+ local log_type_="$1"
67+ local log_format_="$2"
68+ 
69+ if [ $log_type_ = "INFO" ]; then
70+ echo "${log_format_}"
71+ elif [ $log_type_ = "WARNING" ]; then
72+ echo "${log_format_}"
73+ elif [ $log_type_ = "ERROR" ]; then
74+ echo "${log_format_}"
75+ elif [ $log_type_ = "DEBUG" ]; then
76+ :
77+ fi
78+}
79+ 
80+# 输出
81+comm_echo() {
82+ local log_type="$1"
83+ local msg="$2"
84+ local log_msg
85+ 
86+ _comm_compose_log_msg "log_msg" "$log_type" "$msg"
87+ _comm_echo_log_msg "$log_type" "$log_msg"
88+}
89+ 
90+# 写日志
91+comm_log() {
92+ local log_type="$1"
93+ local msg="$2"
94+ local log_msg
95+ 
96+ _comm_compose_log_msg "log_msg" "$log_type" "$msg"
97+ _comm_echo_log_msg "$log_type" "$log_msg"
98+ echo "$log_msg" >> "${LOG_FILE}"
99+}
100+ 
101+# 设置日志参数
102+set_comm_log() {
103+ local pkg_name="$1"
104+ local log_file="$2"
105+ 
106+ LOG_PKG_NAME="${pkg_name}"
107+ if [ "$log_file" != "" ]; then
108+ LOG_FILE="${log_file}"
109+ fi
110+}
111+ 
112+# 安全日志
113+comm_log_operation() {
114+ local cur_date="$(date +'%Y-%m-%d %H:%M:%S')"
115+ local operation="$1"
116+ local runfilename="$2"
117+ local result="$3"
118+ local installmode="$4"
119+ local all_parma="$5"
120+ local level=""
121+ if [ "${operation}" = "Install" ]; then
122+ level="SUGGESTION"
123+ elif [ "${operation}" = "Upgrade" ]; then
124+ level="MINOR"
125+ elif [ "${operation}" = "Uninstall" ]; then
126+ level="MAJOR"
127+ else
128+ level="UNKNOWN"
129+ fi
130+ 
131+ if [ ! -f "${COMM_OPERATION_LOGFILE}" ]; then
132+ touch "${COMM_OPERATION_LOGFILE}"
133+ chmod 640 "${COMM_OPERATION_LOGFILE}"
134+ fi
135+ 
136+ echo "${operation} ${level} ${COMM_USERNAME} ${cur_date} 127.0.0.1 ${runfilename} ${result} installmode=${installmode}; cmdlist=${all_parma}" >> "${COMM_OPERATION_LOGFILE}"
137+}
138+ 
139+## end module
140+ 
141+# 转换--install-for-all参数下文件权限
142+comm_set_install_for_all_mod() {
143+ local _outvar="$1"
144+ local _mod="$2"
145+ local _new_mod
146+ 
147+ # ${parameter%word} Remove matching suffix pattern.
148+ local _new_mod="${_mod%?}"
149+ # ${parameter#word} Remove matching prefix pattern.
150+ local _new_mod="${_new_mod}${_new_mod#${_new_mod%?}}"
151+ 
152+ eval "${_outvar}=\"${_new_mod}\""
153+}
154+ 
155+# 创建文件
156+comm_create_file() {
157+ local path="$1"
158+ local mod="$2"
159+ local own="$3"
160+ local install_for_all="$4"
161+ 
162+ if [ -d "$path" ]; then
163+ comm_log "WARNING" "remove existed dir $path before create file."
164+ rm -rf "$path"
165+ fi
166+ touch "$path"
167+ if [ $? -ne 0 ]; then
168+ comm_log "WARNING" "create file $path failed."
169+ return 1
170+ fi
171+ 
172+ if [ "$install_for_all" = "true" ] || [ "$install_for_all" = "y" ]; then
173+ comm_set_install_for_all_mod "mod" "$mod"
174+ fi
175+ chmod "$mod" "$path"
176+ if [ $? -ne 0 ]; then
177+ comm_log "WARNING" "chmod path $path $mod failed."
178+ return 1
179+ fi
180+ 
181+ chown "$own" "$path"
182+ if [ $? -ne 0 ]; then
183+ comm_log "WARNING" "chown path $path $own failed."
184+ return 1
185+ fi
186+ return 0
187+}
188+ 
189+# 创建目录
190+comm_create_dir() {
191+ local path="$1"
192+ local mod="$2"
193+ local own="$3"
194+ local install_for_all="$4"
195+ 
196+ if [ "$path" = "" ]; then
197+ comm_log "WARNING" "dir path is empty"
198+ return 1
199+ fi
200+ 
201+ if [ ! -d "$path" ]; then
202+ mkdir -p "$path"
203+ if [ $? -ne 0 ]; then
204+ comm_log "WARNING" "create dir $path failed."
205+ return 1
206+ fi
207+ fi
208+ 
209+ if [ "$install_for_all" = "true" ] || [ "$install_for_all" = "y" ]; then
210+ comm_set_install_for_all_mod "mod" "$mod"
211+ fi
212+ chmod "$mod" "$path"
213+ if [ $? -ne 0 ]; then
214+ comm_log "WARNING" "chmod path $path $mod failed."
215+ return 1
216+ fi
217+ 
218+ chown -f "$own" "$path"
219+ if [ $? -ne 0 ]; then
220+ comm_log "WARNING" "chown path $path $own failed."
221+ return 1
222+ fi
223+}
224+ 
225+# 创建子包目录
226+comm_create_package_dir() {
227+ local package_dir="$1"
228+ local own="$2"
229+ local install_for_all="$3"
230+ if [ ! -d "$package_dir" ]; then
231+ comm_create_dir "$package_dir" "755" "$own" "$install_for_all"
232+ fi
233+}
234+ 
235+# 创建install_info文件
236+comm_create_install_info_by_path() {
237+ local install_info="$1"
238+ local own="$2"
239+ if [ ! -f "$install_info" ]; then
240+ comm_create_file "$install_info" "640" "$own" "false"
241+ fi
242+}
243+ 
244+# 更新安装参数
245+comm_update_install_param() {
246+ local key="$1"
247+ local val="$2"
248+ local file="$3"
249+ local param=""
250+ if [ ! -f "${file}" ]; then
251+ return 1
252+ fi
253+ param="$(grep -i "^${key}=" "${file}")"
254+ if [ "${param}" = "" ]; then
255+ echo "${key}=${val}" >> "${file}"
256+ else
257+ sed -i "/^${key}=/Ic ${key}=${val}" "${file}"
258+ fi
259+}
260+ 
261+# 获取安装参数
262+# _outvar : [输出变量],安装参数值
263+# _file : install.info文件路径
264+# _key : 参数名
265+comm_get_install_param() {
266+ local _outvar="$1"
267+ local _file="$2"
268+ local _key="$3"
269+ local _result
270+ 
271+ if [ ! -f "${_file}" ]; then
272+ comm_log "WARNING" "file ${_file} doesn't exist in get install param."
273+ return 1
274+ fi
275+ _result="$(grep -i "^${_key}=" "${_file}" | cut -d"=" -f2-)"
276+ eval "${_outvar}=\"${_result}\""
277+}
278+ 
279+# 更新安装参数文件
280+comm_update_install_info() {
281+ local install_path_param=""
282+ local install_type=""
283+ local username=""
284+ local usergroup=""
285+ local feature_type=""
286+ local package install_info
287+ 
288+ while true
289+ do
290+ case "$1" in
291+ --install-path-param=*)
292+ install_path_param="$(echo "$1" | cut -d"=" -f2-)"
293+ shift
294+ ;;
295+ --install-type=*)
296+ install_type="$(echo "$1" | cut -d"=" -f2-)"
297+ shift
298+ ;;
299+ --username=*)
300+ username="$(echo "$1" | cut -d"=" -f2-)"
301+ shift
302+ ;;
303+ --usergroup=*)
304+ usergroup="$(echo "$1" | cut -d"=" -f2-)"
305+ shift
306+ ;;
307+ --feature-type=*)
308+ feature_type="$(echo "$1" | cut -d"=" -f2-)"
309+ shift
310+ ;;
311+ *)
312+ break
313+ ;;
314+ esac
315+ done
316+ 
317+ if [ $# -lt 2 ]; then
318+ return 1
319+ fi
320+ 
321+ package="$1"
322+ install_info="$2"
323+ 
324+ if [ "$install_path_param" != "" ]; then
325+ comm_update_install_param "${package}_Install_Path_Param" "$install_path_param" "$install_info"
326+ fi
327+ if [ "$install_type" != "" ]; then
328+ comm_update_install_param "${package}_Install_Type" "$install_type" "$install_info"
329+ fi
330+ if [ "$username" != "" ]; then
331+ comm_update_install_param "${package}_UserName" "$username" "$install_info"
332+ fi
333+ if [ "$usergroup" != "" ]; then
334+ comm_update_install_param "${package}_UserGroup" "$usergroup" "$install_info"
335+ fi
336+ if [ "$feature_type" != "" ]; then
337+ comm_update_install_param "${package}_Feature_Type" "$feature_type" "$install_info"
338+ fi
339+}
340+ 
341+# 开始安装前打印开始信息
342+comm_start_log() {
343+ local all_parma="$@"
344+ local cur_date="$(date +'%Y-%m-%d %H:%M:%S')"
345+ comm_log "INFO" "Start time:$cur_date"
346+ comm_log "INFO" "LogFile:${COMM_LOGFILE}"
347+ comm_log "INFO" "InputParams:$all_parma"
348+ comm_log "INFO" "OperationLogFile:${COMM_OPERATION_LOGFILE}"
349+}
350+ 
351+# 安装结束退出前打印结束信息
352+comm_exit_log() {
353+ local cur_date="$(date +'%Y-%m-%d %H:%M:%S')"
354+ comm_log "INFO" "End time:${cur_date}"
355+ exit "$1"
356+}
357+ 
358+comm_print_usage() {
359+ local runfilename="$1"
360+ comm_log "INFO" "Please input this command for help: ./${runfilename} --help"
361+}
362+ 
363+# 判断安装路径是否合法
364+comm_judgmentpath() {
365+ local install_path="$1"
366+ local pkg_name="$2"
367+ check_install_path_valid "$install_path"
368+ if [ $? -ne 0 ]; then
369+ comm_log "ERROR" "The $pkg_name install_path $install_path is invalid, only characters in [a-z,A-Z,0-9,-,_] are supported!"
370+ exit 1
371+ fi
372+}
373+ 
374+# 标准化安装路径
375+# _outvar : [输出变量],处理后的安装路径
376+# _install_path : 安装路径
377+comm_normalize_install_path() {
378+ local _outvar="$1"
379+ local _install_path="$2"
380+ local _slashes_num _result
381+ _slashes_num=$(echo "$_install_path" | grep -o '/' | wc -l)
382+ if [ "$_slashes_num" -gt 1 ]; then
383+ _result=$(echo "$_install_path" | sed "s/\/*$//g")
384+ else
385+ _result="$_install_path"
386+ fi
387+ eval "${_outvar}=\"${_result}\""
388+}
389+ 
390+# 解析安装路径
391+# _outvar : [输出变量],处理后的安装路径
392+# _install_path : 安装路径
393+# _pkg_name : 包名
394+comm_parse_install_path() {
395+ local _outvar="$1"
396+ local _install_path="$2"
397+ local _pkg_name="$3"
398+ 
399+ comm_judgmentpath "$_install_path" "$_pkg_name"
400+ comm_normalize_install_path "$_outvar" "$_install_path"
401+}
402+ 
403+############### 错误函数 ###############
404+# 文件没有找到
405+comm_err_file_or_directory_not_exist() {
406+ comm_log "ERROR" "The file or directory doesn't exist, $1"
407+ comm_exit_log 1
408+}
409+ 
410+os_name() {
411+ if [ ! -f "$1" ];then
412+ HostOsName=unknown
413+ HostOsFullName=unknown
414+ HostOsVersion=unknown
415+ return
416+ fi
417+ 
418+ HostOsName=$(cat "$1" | grep ^NAME= | awk -F "[\" ]" '{print $2}')
419+ HostOsFullName=$(cat "$1" | grep ^NAME= | awk -F "\"" '{print $2}')
420+ 
421+ if [ x"$HostOsName" = "x" ];then
422+ HostOsName=$(cat "$1" | grep ^NAME= | awk -F "[=]" '{print $2}')
423+ HostOsFullName=$(cat "$1" | grep ^NAME= | awk -F "\"" '{print $2}')
424+ fi
425+ if [ x"$HostOsName" = "x" ];then
426+ HostOsName=unknown
427+ HostOsFullName=unknown
428+ fi
429+ HostOsVersion=$(cat "$1" | grep ^VERSION_ID= | awk -F "\"" '{print $2}')
430+ if [ x"$HostOsVersion" = "x" ];then
431+ HostOsVersion=unknown
432+ fi
433+ return
434+}
435+ 
436+get_os_info() {
437+ if [ -f /etc/os-release ];then
438+ os_name /etc/os-release
439+ elif [ -f /etc/centos-release ];then
440+ HostOsName=CentOS
441+ HostOsFullName="CentOS Linux"
442+ HostOsVersion=$(cat /etc/centos-release | awk '{print $4}')
443+ else
444+ which lsb_release >/dev/null 2>&1
445+ if [ $? -eq 0 ];then
446+ HostOsName=$(lsb_release -si)
447+ HostOsFullName="${HostOsName}"
448+ HostOsVersion=$(lsb_release -sr)
449+ else
450+ os_name /etc/issue
451+ fi
452+ fi
453+ return
454+}
455+ 
456+get_system_info() {
457+ get_os_info
458+ HostArch=$(uname -m)
459+ KernelVersion=$(uname -r)
460+}
461+ 
462+version_gt() {
463+ if [ "$2"x = "x" ];then
464+ return 0
465+ else
466+ test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"
467+ fi
468+}
469+version_le() {
470+ if [ "$2"x = "x" ];then
471+ return 0
472+ else
473+ test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" = "$1"
474+ fi
475+}
476+version_lt() {
477+ if [ "$2"x = "x" ];then
478+ return 0
479+ else
480+ test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"
481+ fi
482+}
483+version_ge() {
484+ if [ "$2"x = "x" ];then
485+ return 0
486+ else
487+ test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" = "$1"
488+ fi
489+}
490+ 
491+version_vaild() {
492+ local ver_range="$1" #version range
493+ local ver="$2" #version provider
494+ local clean_ver_range=$(echo "$ver_range" | sed 's/"//g') # clean ver_range,去除引号啥的
495+ local new_ver_range=$(echo "$clean_ver_range" | sed 's/\[//' | sed 's/\]//' | sed 's/(//' | sed 's/)//') #处理过的version range,去除[],() 得到类似 1.0,2.0
496+ 
497+ local start=$(echo $new_ver_range | awk -F ',' '{print $1}')
498+ local end=$(echo $new_ver_range | awk -F ',' '{print $2}')
499+ 
500+ if echo $clean_ver_range | grep -Eq "^\[.*\]$" ; then
501+ # 闭合区间 [a,b]={x|a<=x<=b}
502+ if version_ge $ver $start && version_le $ver $end ; then
503+ # pass
504+ return 0
505+ fi
506+ elif echo $clean_ver_range | grep -Eq "^\[.*\)$"; then
507+ # 左闭右开 [a,b)={x|a<=x<b}
508+ if version_ge $ver $start && version_lt $ver $end ; then
509+ return 0
510+ fi
511+ elif echo $clean_ver_range | grep -Eq "^\(.*\]$"; then
512+ # 左开右闭 (a,b]={x|a<x<=b}
513+ if version_gt $ver $start && version_le $ver $end ; then
514+ return 0
515+ fi
516+ elif echo $clean_ver_range | grep -Eq "^\(.*\)$"; then
517+ # 开区间 (a,b)={x|a<x<b}
518+ if version_gt $ver $start && version_lt $ver $end ; then
519+ return 0
520+ fi
521+ else
522+ # 兼容老版本,ver_range=require_ver,ver >= require_ver 结果pass 否则 nopass。即依赖包的当前版本大于等于需求
523+ if version_ge $ver $clean_ver_range ;then
524+ return 0
525+ fi
526+ fi
527+ #not pass
528+ return 1
529+}
530+ 
531+# 检查包接口版本。
532+# 输出VerCheckStatus和ver_check_status变量。
533+check_pkg_ver_deps() {
534+ ver_path="$1"
535+ req_pkg="$2"
536+ req_ver_path="$3"
537+ 
538+ ver_info_list=$(awk -F '[_=]' '$1=="required" && $2=="'$req_pkg'" {print $1":"$2":"$3":"$4":"$5}' "$ver_path")
539+ 
540+ for line in ${ver_info_list}
541+ do
542+ itf=`echo $line | cut -d":" -f 3`
543+ ver=`echo $line | cut -d":" -f 5`
544+ req_ver=`awk -F '=' '$1=="'$itf'_version" {print $2}' "$req_ver_path"`
545+ if ! version_vaild $ver $req_ver; then
546+ VerCheckStatus=FAIL
547+ ver_check_status=FAIL
548+ return
549+ fi
550+ done
551+ VerCheckStatus=SUCC
552+ ver_check_status=SUCC
553+}
554+ 
555+# 检查安装路径是否合法。
556+# install_path : 安装路径
557+check_install_path_valid() {
558+ local install_path="$1"
559+ # 黑名单设置,不允许//,...这样的路径
560+ if echo "${install_path}" | grep -Eq '/{2,}|\.{3,}'; then
561+ return 1
562+ fi
563+ # 白名单设置,只允许常见字符
564+ if echo "${install_path}" | grep -Eq '^~?[a-zA-Z0-9./_-]*$'; then
565+ return 0
566+ else
567+ return 1
568+ fi
569+}
570+ 
571+# 创建相对软链接
572+# mdc的release包场景,ln命令不支持-r参数,需要手动实现相对软链功能
573+# 入参需要是规范(绝对)路径,完整路径。例如:目标路径不能是一个目录,否则相对软链计算结果不正确
574+# src_path : 源路径
575+# dst_path : 目标路径
576+create_softlink_icp_relative() {
577+ local src_path="$1"
578+ local dst_path="$2"
579+ local source="top${src_path}"
580+ local target="top${dst_path}"
581+ 
582+ # 若变量内容从尾向前的数据符合,则将符合的最短数据删除
583+ local common="${target%/*}"
584+ # 若变量内容从头开始的数据符合,则将符合的最短数据删除
585+ local forward="${source#"$common"/}"
586+ 
587+ local result=""
588+ 
589+ while [ "${forward}" = "${source}" ]; do
590+ common="$(dirname "$common")"
591+ forward="${source#"$common"/}"
592+ result="../${result}"
593+ done
594+ 
595+ result="${result}${forward}"
596+ 
597+ ln -sfn "${result}" "${dst_path}"
598+ if [ $? -ne 0 ]; then
599+ log "ERROR" "create softlink relative from ${src_path} to ${dst_path} failed!"
600+ return 1
601+ fi
602+ return 0
603+}
604+ 
605+# 创建软链接
606+create_softlink() {
607+ local _src_dir="$1"
608+ local _dst_dir="$2"
609+ local _sub_dir_src="$3"
610+ local _sub_dir_dst="$4"
611+ local _sub_dir_dst_new _dst_path ret total_ret=0
612+ 
613+ # 注意_sub_dir_src可能有通配符
614+ for _src_path in "${_src_dir}"/${_sub_dir_src}
615+ do
616+ if [ -z "$_sub_dir_dst" ]; then
617+ _sub_dir_dst_new="$(basename "${_src_path}")"
618+ else
619+ _sub_dir_dst_new="${_sub_dir_dst}"
620+ fi
621+ _dst_path="${_dst_dir}/${_sub_dir_dst_new}"
622+ if [ -L "${_dst_path}" ]; then
623+ rm -f "${_dst_path}"
624+ fi
625+ # 目标取规范化路径
626+ _dst_path="$(readlink -f "${_dst_path}")"
627+ if [ -d "${_dst_path}" ]; then
628+ # 如果目标为目录,拼接上源文件名
629+ _dst_path="${_dst_path}/$(basename "${_src_path}")"
630+ fi
631+ 
632+ # 源取规范化路径
633+ _src_path="$(readlink -f "${_src_path}")"
634+ 
635+ create_softlink_icp_relative "${_src_path}" "${_dst_path}"
636+ ret="$?" && [ $ret -ne 0 ] && total_ret=$ret
637+ done
638+ 
639+ return $total_ret
640+}
641+ 
642+# 创建软链接,调用ln -r命令
643+create_softlink_by_relative_ln() {
644+ local _src_dir="$1"
645+ local _dst_dir="$2"
646+ local _sub_dir_src="$3"
647+ local _sub_dir_dst="$4"
648+ local _src_path="$_src_dir/$_sub_dir_src"
649+ # 注意_src_path可能有通配符,处理流程中需要考虑通配场景。
650+ local _dst_path
651+ if [ -z "$_sub_dir_dst" ]; then
652+ _sub_dir_dst=$(basename $_src_path)
653+ fi
654+ _dst_path="$_dst_dir/$_sub_dir_dst"
655+ if [ -L "${_dst_path}" ]; then
656+ rm -f "${_dst_path}"
657+ fi
658+ ln -sr $_src_path $_dst_path
659+}
660+ 
661+# 如果源文件存在,则创建软链接
662+create_softlink_if_exists() {
663+ local _src_dir="$1"
664+ local _sub_dir_src="$3"
665+ local _src_path="$_src_dir/$_sub_dir_src"
666+ 
667+ if [ -e $_src_path ]; then
668+ create_softlink "$@"
669+ fi
670+}
671+ 
672+# 目录是否为空
673+# 空目录返回0
674+# 目录不存在返回1
675+# 目录非空或无权限访问返回2
676+is_dir_empty() {
677+ # 如果目录不存在,则返回
678+ if [ ! -d "$1" ]; then
679+ return 1
680+ fi
681+ # 否则检查目录是否为空
682+ # 2>&1重定向无权限访问报错信息
683+ if [ "$(ls -A "$1" 2>&1)" != "" ]; then
684+ return 2
685+ fi
686+ return 0
687+}
688+ 
689+# 删除空目录
690+remove_dir_if_empty() {
691+ local dirpath="$1"
692+ 
693+ if is_dir_empty "${dirpath}"; then
694+ rm -rf "${dirpath}"
695+ fi
696+ return 0
697+}
698+ 
699+# 删除目录
700+remove_dir() {
701+ local dirpath="$1"
702+ 
703+ if [ -e "${dirpath}" ]; then
704+ chmod u+w -R "${dirpath}"
705+ fi
706+ rm -rf "${dirpath}"
707+ 
708+ return 0
709+}
710+ 
711+# 删除软链接
712+remove_softlink() {
713+ local path="$1"
714+ [ -L "${path}" ] && rm -f "${path}"
715+}
716+ 
717+# 处理pre_check
718+# pkg_name : 包名
719+# pre_check_func : pre_check函数
720+# standalone : 是否为独立的pre_check命令[y/n]
721+process_pre_check() {
722+ local pkg_name="$1"
723+ local pre_check_func="$2"
724+ local standalone="$3"
725+ 
726+ log "INFO" "${pkg_name} do pre_check started."
727+ ${pre_check_func}
728+ ret=$?
729+ if [ $ret -ne 0 ]; then
730+ log "WARNING" "${pkg_name} do pre check failed."
731+ fi
732+ log "INFO" "${pkg_name} do pre_check finished."
733+ 
734+ if [ "${standalone}" = "y" ]; then
735+ if [ $ret -ne 0 ]; then
736+ exitInstallLog 1
737+ fi
738+ exitInstallLog 0
739+ fi
740+}
741+ 
742+# 包是否在环境上安装
743+# install_path : 安装路径
744+# pkgname : 包名
745+# varname : [输出变量],包是否在环境上安装
746+does_pkg_installed() {
747+ local install_path="$1"
748+ local pkgname="$2"
749+ local varname="$3"
750+ local result="false"
751+ local pkg_path="${install_path}/share/info/${pkgname}"
752+ 
753+ if [ -d "${pkg_path}" ]; then
754+ result="true"
755+ fi
756+ 
757+ read -r "$varname" <<EOF
758+$result
759+EOF
760+}
761+ 
762+ 
763+# 获取驱动包安装路径
764+# 搜索顺序:
765+# 1. 查看同级目录下是否存在driver包
766+# 2. 查看/etc/ascend_install.info中配置的路径
767+# _outvar : [输出变量],driver包安装路径
768+# _install_path : 安装路径
769+get_driver_install_path() {
770+ local _outvar="$1"
771+ local _install_path="$2"
772+ 
773+ local _install_info="/etc/ascend_install.info"
774+ local _ascend_hal_name="libascend_hal.so"
775+ local _old_ifs
776+ local _var
777+ local _driver_install_path
778+ local _ldconfig_result
779+ local _ldconfig_cnt
780+ 
781+ if [ -d "${_install_path}/driver" ]; then
782+ eval "${_outvar}=\"${_install_path}\""
783+ return 0
784+ fi
785+ 
786+ # 第一种方案
787+ if [ -f ${_install_info} ]; then
788+ . ${_install_info}
789+ if [ ! -z ${Driver_Install_Path_Param} ]; then
790+ _driver_install_path="${Driver_Install_Path_Param}"
791+ eval "${_outvar}=\"${_driver_install_path}\""
792+ return 0
793+ fi
794+ fi
795+ 
796+ eval "${_outvar}=\"\""
797+}
798+ 
799+# 执行卸载脚本
800+# install_path : 安装路径
801+# pkgname : 包名
802+run_uninstall_script() {
803+ local install_path="$1"
804+ local pkgname="$2"
805+ local uninstall_path="${install_path}/${pkgname}/script/uninstall.sh"
806+ 
807+ if [ -f "${uninstall_path}" ]; then
808+ "${uninstall_path}"
809+ return $?
810+ else
811+ return 1
812+ fi
813+}
814+ 
815+# 获取包版本。
816+get_package_version() {
817+ local _outvar="$1"
818+ local _version_info_path="$2"
819+ local _result
820+ 
821+ if [ ! -f "${_version_info_path}" ]; then
822+ eval "${_outvar}=\"\""
823+ return 1
824+ fi
825+ 
826+ _result="$(grep "^Version=" "${_version_info_path}" | cut -d= -f2-)"
827+ eval "${_outvar}=\"${_result}\""
828+}
829+ 
830+# 获取包版本
831+# _outvar : [输出变量],包版本
832+# _version_info_path : version.info文件路径
833+get_version() {
834+ get_package_version "$@"
835+}
836+ 
837+# 获取包版本目录
838+# _outvar : [输出变量],包版本目录
839+# _version_info_path : version.info文件路径
840+get_version_dir() {
841+ local _outvar="$1"
842+ local _version_info_path="$2"
843+ local _result
844+ 
845+ if [ ! -f "${_version_info_path}" ]; then
846+ eval "${_outvar}=\"\""
847+ return 1
848+ fi
849+ 
850+ _result="$(grep "^version_dir=" "${_version_info_path}" | cut -d= -f2-)"
851+ eval "${_outvar}=\"${_result}\""
852+}
853+ 
854+# 获取包架构
855+# _outvar : [输出变量],包架构
856+# _scene_info_path : scene.info文件路径
857+get_arch() {
858+ local _outvar="$1"
859+ local _scene_info_path="$2"
860+ local _result
861+ 
862+ if [ ! -f "${_scene_info_path}" ]; then
863+ eval "${_outvar}=\"\""
864+ return 1
865+ fi
866+ 
867+ _result="$(grep "^arch=" "${_scene_info_path}" | cut -d= -f2-)"
868+ eval "${_outvar}=\"${_result}\""
869+}
870+ 
871+# 获取包目录
872+# _outvar : [输出变量],包目录
873+# _version_info_path : version.info文件路径
874+# _install_path : 安装路径
875+# _pkg_name : 包名
876+get_install_package_dir() {
877+ local _outvar="$1"
878+ local _version_info_path="$2"
879+ local _install_path="$3"
880+ local _pkg_name="$4"
881+ local _pkg_is_multi_version _pkg_version_dir _result
882+ 
883+ is_multi_version_pkg "_pkg_is_multi_version" "$_version_info_path"
884+ get_version_dir "_pkg_version_dir" "$_version_info_path"
885+ 
886+ if [ "$_pkg_is_multi_version" = "true" ]; then
887+ _result="$_install_path/$_pkg_version_dir/$_pkg_name"
888+ else
889+ _result="$_install_path/$_pkg_name"
890+ fi
891+ eval "${_outvar}=\"${_result}\""
892+}
893+ 
894+# 包名转换为日志输出的包名(首字母大写)
895+_package_to_log_pkg_name() {
896+ local _outvar="$1"
897+ local _pkg_name="$2"
898+ 
899+ local _result
900+ 
901+ _result="$(awk -v pkg_name="${_pkg_name}" '
902+ BEGIN {print toupper(substr(pkg_name, 1, 1)) substr(pkg_name, 2)}
903+ ')"
904+ 
905+ eval "${_outvar}=\"${_result}\""
906+}
907+ 
908+# 获取首字母大字的包名
909+get_titled_package_name() {
910+ _package_to_log_pkg_name "$@"
911+}
912+ 
913+# 安装前处理。
914+# 返回码:
915+# 0: 成功
916+# 1: 版本兼容性检查失败
917+# 调用方式:
918+# preinstall_process --install-path=<install_path> --script-dir=<script-dir> --package=<package> --logfile=<logfile> [ --logstyle=<logstyle> ] [ --docker-root=<docker_root> ]
919+#
920+# --install-path=<install_path> : 安装路径
921+# --script-dir=<script-dir> : 当前脚本目录路径
922+# --package=<package> : 当前包名
923+# --logfile=<logfile> : ascend_install.log日志文件路径
924+# --logstyle=<logstyle> : 日志风格
925+# --docker-root=<docker_root> : docker根路径
926+preinstall_process() {
927+ local install_path=""
928+ local docker_root=""
929+ local script_dir=""
930+ local package=""
931+ local logfile=""
932+ local real_install_path
933+ local version_info_path
934+ local ret
935+ 
936+ # 参数列表末尾添加--参数
937+ eval set -- "$@ --"
938+ 
939+ while true
940+ do
941+ case "$1" in
942+ --install-path=*)
943+ install_path="$(echo "$1" | cut -d"=" -f2-)"
944+ shift
945+ ;;
946+ --docker-root=*)
947+ docker_root="$(echo "$1" | cut -d"=" -f2-)"
948+ shift
949+ ;;
950+ --script-dir=*)
951+ script_dir="$(echo "$1" | cut -d"=" -f2-)"
952+ shift
953+ ;;
954+ --package=*)
955+ package="$(echo "$1" | cut -d"=" -f2-)"
956+ shift
957+ ;;
958+ --logfile=*)
959+ logfile="$(echo "$1" | cut -d"=" -f2-)"
960+ shift
961+ ;;
962+ --logstyle=*)
963+ LOG_STYLE="$(echo "$1" | cut -d"=" -f2-)"
964+ shift
965+ ;;
966+ *)
967+ break
968+ ;;
969+ esac
970+ done
971+ 
972+ if [ "${install_path}" = "" ]; then
973+ echo "error: install_path is needed!"
974+ return 1
975+ fi
976+ if [ "${script_dir}" = "" ]; then
977+ echo "error: script_dir is needed!"
978+ return 1
979+ fi
980+ 
981+ if [ "${package}" = "" ]; then
982+ echo "error: package is needed!"
983+ return 1
984+ fi
985+ if [ "${logfile}" = "" ]; then
986+ echo "error: logfile is needed!"
987+ return 1
988+ fi
989+ 
990+ LOG_FILE="${logfile}"
991+ _package_to_log_pkg_name "LOG_PKG_NAME" "${package}"
992+ 
993+ if [ "${docker_root}" = "" ]; then
994+ real_install_path="${install_path}"
995+ else
996+ real_install_path="${docker_root}/${install_path}"
997+ fi
998+ 
999+ _check_version_compatiable "$package" "${real_install_path}" "${script_dir}"
1000+ if [ $? -ne 0 ]; then
1001+ return 1
1002+ fi
1003+ 
1004+ return 0
1005+}
1006+ 
1007+# 安装前检查。
1008+# 返回码:
1009+# 0: 成功
1010+# 1: 版本兼容性检查失败
1011+# 调用方式:
1012+# preinstall_check --install-path=<install_path> --script-dir=<script-dir> --package=<package> --logfile=<logfile> [ --logstyle=<logstyle> ] [ --docker-root=<docker_root> ]
1013+#
1014+# --install-path=<install_path> : 安装路径
1015+# --script-dir=<script-dir> : 当前脚本目录路径
1016+# --package=<package> : 当前包名
1017+# --logfile=<logfile> : ascend_install.log日志文件路径
1018+# --logstyle=<logstyle> : 日志风格
1019+# --docker-root=<docker_root> : docker根路径
1020+preinstall_check() {
1021+ preinstall_process "$@"
1022+}
1023+ 
1024+# 是否为多版本包
1025+# 读取version.info文件,检查本包是否为多版本包
1026+# _outvar : [输出变量],是否为多版本包
1027+# _filepath : version.info文件路径
1028+is_multi_version_pkg() {
1029+ local _outvar="$1"
1030+ local _filepath="$2"
1031+ local _ret="false"
1032+ 
1033+ if [ -f "${_filepath}" ]; then
1034+ grep "^version_dir=" "${_filepath}" > /dev/null
1035+ [ $? -eq 0 ] && _ret="true"
1036+ fi
1037+ eval "${_outvar}=\"${_ret}\""
1038+}
1039+ 
1040+# 文件列表的某一列
1041+_filelist_column() {
1042+ local _filepath="$1"
1043+ local _idx="$2"
1044+ tail -n +2 "$_filepath" | cut -d, -f${_idx} | sort | uniq
1045+}
1046+ 
1047+# 移除comm特性
1048+_remove_comm_feature() {
1049+ local _outvar="$1"
1050+ local _feature="$2"
1051+ local _result
1052+ 
1053+ _result="$(echo "$_feature" | sed 's/comm//g' | sed 's/,,/,/g' | sed 's/^,//g' | sed 's/,$//g')"
1054+ eval "${_outvar}=\"${_result}\""
1055+}
1056+ 
1057+# 特性列表转换为正则表达式
1058+_feature_to_regex() {
1059+ local _outvar="$1"
1060+ local _feature="$2"
1061+ local _result_ftr
1062+ _result_ftr="\b(($(echo "$_feature" | sed 's/,/)|(/g')))\b"
1063+ eval "${_outvar}=\"${_result_ftr}\""
1064+}
1065+ 
1066+# 是否包含指定feature
1067+# 读取filelist.csv文件,检查是否包含指定feature
1068+# _outvar : [输出变量](true/false),是否包含指定feature
1069+# _feature : 指定feature,可指定多个,以“,”分隔,如:dvpp,audio
1070+# _filepath : filelist.csv文件路径
1071+contain_feature() {
1072+ local _outvar="$1"
1073+ local _feature="$2"
1074+ local _filepath="$3"
1075+ local _feature_regex _feature_cf
1076+ 
1077+ # 输入的comm不作为特性,也就是说不支持--feature=comm
1078+ _remove_comm_feature "_feature_cf" "$_feature"
1079+ if [ "$_feature_cf" = "" ]; then
1080+ eval "${_outvar}=\"false\""
1081+ return 0
1082+ fi
1083+ 
1084+ _feature_to_regex "_feature_regex" "$_feature_cf"
1085+ _filelist_column "$_filepath" 10 | grep -E "$_feature_regex" > /dev/null 2>&1
1086+ if [ $? -eq 0 ]; then
1087+ eval "${_outvar}=\"true\""
1088+ else
1089+ eval "${_outvar}=\"false\""
1090+ fi
1091+}
1092+ 
1093+# 向filelist.csv中写入1个条目
1094+# filepath : filelist.csv文件路径
1095+# operation : 操作类型
1096+# path_in_pkg : 包内路径
1097+# install_path : 安装路径
1098+# permission : 文件权限
1099+# owner_group : 文件属主,支持默认值DEFAULT
1100+# install_type : 安装类型
1101+# softlink : 软链接
1102+# block : 所属块
1103+# feature : 所属特性,支持默认值DEFAULT
1104+# chip : 所属芯片,支持默认值DEFAULT
1105+# arch : 包架构
1106+add_fileitem() {
1107+ local filepath="$1"
1108+ local operation="$2"
1109+ local path_in_pkg="$3"
1110+ local install_path="$4"
1111+ local permission="$5"
1112+ local owner_group="$6"
1113+ local install_type="$7"
1114+ local softlink="$8"
1115+ local pkg_inner_softlink="$9"
1116+ local block="${10}"
1117+ local feature="${11}"
1118+ local chip="${12}"
1119+ local arch="${13}"
1120+ local is_common_path="N"
1121+ 
1122+ if [ "$owner_group" = "DEFAULT" ]; then
1123+ owner_group="\\\\\$username:\\\\\$usergroup"
1124+ fi
1125+ 
1126+ if [ "$feature" = "DEFAULT" ]; then
1127+ feature="all"
1128+ fi
1129+ 
1130+ if [ "$chip" = "DEFAULT" ]; then
1131+ chip="all"
1132+ fi
1133+ 
1134+ if echo "$install_path" | grep "^${arch}-linux/" > /dev/null; then
1135+ is_common_path="Y"
1136+ fi
1137+ 
1138+ echo "NA,${operation},${path_in_pkg},${install_path},FALSE,${permission},${owner_group},${install_type},${softlink},${feature},${is_common_path},FALSE,NA,${block},${pkg_inner_softlink},${chip}" >> ${filepath}
1139+}
1140+ 
1141+# 设置公共变量
1142+set_global_vars() {
1143+ local arch
1144+ local scene_filepath="${curpath}/../scene.info"
1145+ 
1146+ if [ -f "${scene_filepath}" ]; then
1147+ get_scene_arch "arch" "${scene_filepath}"
1148+ if [ "${arch}" != "" ]; then
1149+ PKG_ARCH="${arch}"
1150+ fi
1151+ fi
1152+}
1153+ 
1154+# 获取并发进程数
1155+get_thread_num() {
1156+ local _outvar="$1"
1157+ local _thread_num="$(cat /proc/cpuinfo | grep "^processor" | wc -l)"
1158+ 
1159+ # 未取得CPU核数时,默认为1
1160+ if [ "$_thread_num" = "" ] || [ "$_thread_num" = "0" ]; then
1161+ _thread_num="1"
1162+ fi
1163+ 
1164+ # CPU核数2倍
1165+ eval "${_outvar}=\"$((_thread_num*2))\""
1166+}
1167+ 
1168+# 初始化fifo,用于并发控制
1169+init_fifo() {
1170+ local _outvar="$1"
1171+ local _thread_num="$2"
1172+ local _tmppath_if tmp
1173+ local _skip_msg="skip init fifo"
1174+ 
1175+ # 并发数为空时,默认为1
1176+ if [ "$_thread_num" = "" ]; then
1177+ _thread_num="1"
1178+ comm_log "WARNING" "thread number is empty, use ${_thread_num}."
1179+ fi
1180+ # 并发数<=0时,默认为1
1181+ if [ $_thread_num -le 0 ]; then
1182+ comm_log "WARNING" "thread number is ${_thread_num}, use 1."
1183+ _thread_num="1"
1184+ fi
1185+ 
1186+ get_tmp_file _tmppath_if "fifo"
1187+ if [ -e "$_tmppath_if" ]; then
1188+ rm -rf "$_tmppath_if"
1189+ check_ret_warning "$?" "remove old file $_tmppath_if failed, ${_skip_msg}."
1190+ ret="$?" && [ $ret -ne 0 ] && return $ret
1191+ fi
1192+ 
1193+ mkfifo "$_tmppath_if"
1194+ check_ret_warning "$?" "make fifo $_tmppath_if failed, ${_skip_msg}."
1195+ ret="$?" && [ $ret -ne 0 ] && return $ret
1196+ 
1197+ # 使用文件描述符8作并发控制
1198+ exec 8<> "$_tmppath_if" # 创建文件描述符8
1199+ check_ret_warning "$?" "open fifo $_tmppath_if file descriptor failed, ${_skip_msg}."
1200+ ret="$?" && [ $ret -ne 0 ] && return $ret
1201+ 
1202+ rm -f "$_tmppath_if"
1203+ check_ret_warning "$?" "remove fifo $_tmppath_if failed, ${_skip_msg}."
1204+ ret="$?" && [ $ret -ne 0 ] && return $ret
1205+ 
1206+ for tmp in $(seq $_thread_num); do
1207+ echo >&8
1208+ check_ret_warning "$?" "echo fifo $_tmppath_if failed, ${_skip_msg}."
1209+ ret="$?" && [ $ret -ne 0 ] && return $ret
1210+ done
1211+ 
1212+ eval "${_outvar}=\"${_tmppath_if}\""
1213+}
1214+ 
1215+# 根据参数执行
1216+exec_with_param() {
1217+ local param="$1"
1218+ shift 1
1219+ local exec_mode fifo_path="$PARALLEL_FIFO" tmp
1220+ extract_1st "exec_mode" "$param"
1221+ if [ "$exec_mode" = "concurrency" ]; then
1222+ if [ "$fifo_path" = "none" ] || [ "$fifo_path" = "" ]; then
1223+ "$@" &
1224+ else
1225+ read tmp <&8
1226+ {
1227+ "$@"
1228+ ret="$?"
1229+ echo >&8
1230+ exit $ret
1231+ } &
1232+ fi
1233+ else
1234+ "$@"
1235+ fi
1236+}
Ascripts/package/common/sh/common_func_v2.inc+1729-0
@@ -0,0 +1,1729 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+# 公共函数库
13+# 总setenv文件权限
14+SETENV_MOD="550"
15+# 总setenv文件可写状态权限
16+SETENV_WRITEABLE_MOD="600"
17+# 默认特性配置
18+DEFAULT_FEATURE_PARAM="all n all"
19+#export PS4='+ ${FUNCNAME[0]:+${FUNCNAME[0]}():} ${BASH_SOURCE}:${LINENO}: '
20+#set -x
21+RESET_MOD="750"
22+ 
23+# db.info文件格式如下
24+# ---
25+# CommonLib|atc,fwkacllib
26+# Compiler|atc,fwkacllib
27+# ---
28+# 使用"|"分隔模块名和包名列表,包名列表使用","分隔
29+# 文件中块名保持以升序排序
30+PKG_DB_INFO_RELPATH="var/ascend_package_db.info"
31+# 缓存mod文件名
32+STASH_MOD_PATH="stash_mod.txt"
33+# stash_mod文件权限
34+STASH_FILE_MOD="600"
35+ 
36+# 写日志
37+log() {
38+ local cur_date_="$(date +"%Y-%m-%d %H:%M:%S")"
39+ local log_type_="${1}"
40+ local msg_="${2}"
41+ local log_format_="[Common] [${cur_date_}] [${log_type_}]: ${msg_}"
42+ if [ "${log_type_}" = "INFO" ]; then
43+ echo "${log_format_}"
44+ elif [ "${log_type_}" = "WARNING" ]; then
45+ echo "${log_format_}"
46+ elif [ "${log_type_}" = "ERROR" ]; then
47+ echo "${log_format_}"
48+ elif [ "${log_type_}" = "DEBUG" ]; then
49+ echo "${log_format_}" 1> /dev/null
50+ fi
51+}
52+ 
53+# 返回列表长度
54+__length_list() {
55+ local list="$1"
56+ local var="$2"
57+ local list_item
58+ local cnt=0
59+ 
60+ for list_item in ${list}; do
61+ cnt=$((cnt+1))
62+ done
63+ 
64+ eval "${var}=\"${cnt}\""
65+}
66+ 
67+# 获取列表索引值
68+__index_list() {
69+ local list="$1"
70+ shift
71+ local list_item
72+ local cnt=0
73+ 
74+ if [ $# -eq 0 ]; then
75+ return 0
76+ fi
77+ 
78+ for list_item in ${list}; do
79+ if [ ${1} -eq ${cnt} ]; then
80+ eval "${2}=\"${list_item}\""
81+ shift 2
82+ if [ $# -eq 0 ]; then
83+ return 0
84+ fi
85+ fi
86+ cnt=$((cnt+1))
87+ done
88+ 
89+ return 0
90+}
91+ 
92+# 从列表中移除一项
93+__remove_item_in_list() {
94+ local to_removed="$1"
95+ shift
96+ local list="$*"
97+ local list_item
98+ local new_list
99+ 
100+ for list_item in ${list}; do
101+ if [ "${to_removed}" != "${list_item}" ]; then
102+ if [ "${new_list}" = "" ]; then
103+ new_list="${list_item}"
104+ else
105+ new_list="${new_list} ${list_item}"
106+ fi
107+ fi
108+ done
109+ echo "${new_list}"
110+}
111+ 
112+# 元素是否在列表中
113+__item_in_list() {
114+ local _outvar="$1"
115+ local _item="$2"
116+ shift 2
117+ local _list_item
118+ local _matched="false"
119+ 
120+ for _list_item in $*; do
121+ if [ "${_item}" = "${_list_item}" ]; then
122+ _matched="true"
123+ break
124+ fi
125+ done
126+ eval "${_outvar}=\"${_matched}\""
127+}
128+ 
129+# 反转列表
130+__reverse_list() {
131+ local _outvar="$1"
132+ local _list="$2"
133+ local _new_list=""
134+ local _list_item
135+ 
136+ for _list_item in ${_list}; do
137+ if [ "${_new_list}" = "" ]; then
138+ _new_list="${_list_item}"
139+ else
140+ _new_list="${_list_item} ${_new_list}"
141+ fi
142+ done
143+ eval "${_outvar}=\"${_new_list}\""
144+}
145+ 
146+# 修改各文件及目录的属性
147+change_own() {
148+ local recursive="$3"
149+ local option=""
150+ local username="${USERNAME}"
151+ local usergroup="${USERGROUP}"
152+ if [ "$2" != "NA" ]; then
153+ if [ "${recursive}" = "true" ]; then
154+ option="-R"
155+ fi
156+ eval chown ${option} -h \"$2\" \"$1\"
157+ if [ $? -ne 0 ]; then
158+ log "ERROR" "$1 chown failed!"
159+ return 1
160+ fi
161+ fi
162+}
163+ 
164+# 获取install_for_all文件权限
165+get_install_for_all_mod() {
166+ local _outvar="$1"
167+ local _mod="$2"
168+ local _new_mod _other_mod
169+ 
170+ _new_mod="${_mod%?}"
171+ _other_mod="${_new_mod#${_new_mod%?}}"
172+ _other_mod="$(($_other_mod & 5))" # other权限位移除写权限,仅支持普通用户运行
173+ 
174+ eval "${_outvar}=\"${_new_mod}${_other_mod}\""
175+}
176+ 
177+# 修改各文件及目录的权限
178+change_mod() {
179+ local mod="$2"
180+ local install_for_all="$3"
181+ local recursive="$4"
182+ local option="" new_mod
183+ # 对于软连接,可能目标文件还没有拷贝进来,导致无法修改mod,这里过滤掉软连接
184+ if [ -L "$1" ]; then
185+ return 0
186+ fi
187+ if [ "$2" != "NA" ]; then
188+ if [ "${recursive}" = "true" ]; then
189+ option="-R"
190+ fi
191+ # 如果设置了install_for_all,则安装时other权限跟group权限对齐
192+ if [ "${install_for_all}" = "y" ]; then
193+ get_install_for_all_mod new_mod "$mod"
194+ chmod ${option} "${new_mod}" "$1"
195+ else
196+ chmod ${option} "$2" "$1"
197+ fi
198+ if [ $? -ne 0 ]; then
199+ log "ERROR" "$1 chmod failed!"
200+ return 1
201+ fi
202+ fi
203+ return 0
204+}
205+ 
206+# 获取文件权限
207+get_file_mod() {
208+ local _outvar="$1"
209+ local _options="" _ret
210+ shift
211+ 
212+ while true; do
213+ case "$1" in
214+ -L|--dereference)
215+ _options="${_options} $1"
216+ shift
217+ ;;
218+ *)
219+ break
220+ ;;
221+ esac
222+ done
223+ 
224+ local _path="$1"
225+ local _result
226+ 
227+ _result="$(stat ${_options} -c %a "${_path}")"
228+ _ret="$?" && [ $_ret -ne 0 ] && return $_ret
229+ eval "${_outvar}=\"${_result}\""
230+}
231+ 
232+# 检查路径是否为绝对路径
233+__check_abs_path() {
234+ local path="$1"
235+ 
236+ if [ "${path#/}" != "${path}" ]; then
237+ is_abs_path="true"
238+ else
239+ is_abs_path="false"
240+ fi
241+}
242+ 
243+__set_abs_path() {
244+ local install_path="$1"
245+ local path="$2"
246+ local varname="$3"
247+ local is_abs_path
248+ 
249+ __check_abs_path "${path}"
250+ if [ "${is_abs_path}" != "true" ]; then
251+ eval "${varname}=\"${install_path}/${path}\""
252+ else
253+ eval "${varname}=\"${path}\""
254+ fi
255+}
256+ 
257+# 创建目录
258+make_dir() {
259+ local path="$1"
260+ mkdir -p "${path}"
261+ if [ $? -ne 0 ]; then
262+ log "ERROR" "${path} mkdir failed!"
263+ exit 1
264+ fi
265+ return 0
266+}
267+ 
268+# 检查install_path在docker_root之中
269+check_install_path_in_docker_root() {
270+ local install_path="$1"
271+ local docker_root="$2"
272+ 
273+ echo "${install_path}" | grep "^${docker_root}" > /dev/null 2>&1
274+ if [ $? -ne 0 ]; then
275+ log "ERROR" "check install path ${install_path} in docker root ${docker_root} failed!"
276+ return 1
277+ fi
278+ return 0
279+}
280+ 
281+# 移除路径右侧斜线(/)
282+rstrip_path() {
283+ local _outvar="$1"
284+ local _path="$2"
285+ 
286+ _path="$(echo "${_path}" | sed "s/\/\+\$//g")"
287+ eval "${_outvar}=\"${_path}\""
288+}
289+ 
290+# 包是否在tools目录下
291+is_package_under_tools() {
292+ local _outvar="$1"
293+ local _package="$2"
294+ local _result_iput="false"
295+ 
296+ if [ "${_package}" = "aoe" ] || [ "${_package}" = "nca" ] || [ "${_package}" = "amct_acl" ] || [ "${_package}" = "ncs" ]; then
297+ _result_iput="true"
298+ fi
299+ 
300+ eval "${_outvar}=\"${_result_iput}\""
301+}
302+ 
303+# 获取包目录名
304+get_package_dir() {
305+ local _outvar="$1"
306+ local _package="$2"
307+ local _is_under_tools _result
308+ 
309+ if [ "${USE_SHARE_INFO}" == "y" ]; then
310+ _result="share/info"
311+ else
312+ is_package_under_tools "_is_under_tools" "${_package}"
313+ if [ "${_is_under_tools}" = "true" ]; then
314+ _result="tools"
315+ else
316+ _result=""
317+ fi
318+ fi
319+ eval "${_outvar}=\"${_result}\""
320+}
321+ 
322+# 获取包目录路径
323+get_package_dirpath() {
324+ local _outvar="$1"
325+ local _package="$2"
326+ local _is_under_tools _result
327+ 
328+ if [ "${USE_SHARE_INFO}" == "y" ]; then
329+ _result="share/info/${_package}"
330+ else
331+ is_package_under_tools "_is_under_tools" "${_package}"
332+ if [ "${_is_under_tools}" = "true" ]; then
333+ _result="tools/${_package}"
334+ else
335+ _result="${_package}"
336+ fi
337+ fi
338+ eval "${_outvar}=\"${_result}\""
339+}
340+ 
341+# 获取包ascend_install.info路径
342+get_package_install_info() {
343+ local _outvar="$1"
344+ local _install_path="$2"
345+ local _version_dir="$3"
346+ local _package="$4"
347+ local _package_dirpath=""
348+ 
349+ eval "${_outvar}=\"\""
350+ 
351+ get_package_dirpath "_package_dirpath" "${_package}"
352+ 
353+ eval "${_outvar}=\"${_install_path}/${_version_dir}/${_package_dirpath}/ascend_install.info\""
354+}
355+ 
356+# 获取包version.info路径
357+get_package_version_info() {
358+ local _outvar="$1"
359+ local _install_path="$2"
360+ local _version_dir="$3"
361+ local _package="$4"
362+ local _package_dirpath=""
363+ 
364+ eval "${_outvar}=\"\""
365+ 
366+ get_package_dirpath "_package_dirpath" "${_package}"
367+ 
368+ eval "${_outvar}=\"${_install_path}/${_version_dir}/${_package_dirpath}/version.info\""
369+}
370+ 
371+# 获取包filelist.csv路径
372+get_package_filelist() {
373+ local _outvar="$1"
374+ local _install_path="$2"
375+ local _version_dir="$3"
376+ local _package="$4"
377+ local _package_dirpath=""
378+ 
379+ eval "${_outvar}=\"\""
380+ 
381+ get_package_dirpath "_package_dirpath" "${_package}"
382+ 
383+ eval "${_outvar}=\"${_install_path}/${_version_dir}/${_package_dirpath}/script/filelist.csv\""
384+}
385+ 
386+# 获取包install_common_parser.sh路径
387+get_package_install_common_parser() {
388+ local _outvar="$1"
389+ local _install_path="$2"
390+ local _version_dir="$3"
391+ local _package="$4"
392+ local _package_dirpath=""
393+ 
394+ eval "${_outvar}=\"\""
395+ 
396+ get_package_dirpath "_package_dirpath" "${_package}"
397+ 
398+ eval "${_outvar}=\"${_install_path}/${_version_dir}/${_package_dirpath}/script/install_common_parser.sh\""
399+}
400+ 
401+# 获取latest_manager的install_common_parser.sh路径
402+get_latest_manager_install_common_parser() {
403+ local _outvar="$1"
404+ local _install_path="$2"
405+ local _latest_dir="$3"
406+ 
407+ eval "${_outvar}=\"${_install_path}/${_latest_dir}/var/manager/install_common_parser.sh\""
408+}
409+ 
410+# 获取包script目录路径
411+get_package_script_dirpath() {
412+ local _outvar="$1"
413+ local _install_path="$2"
414+ local _version_dir="$3"
415+ local _package="$4"
416+ local _package_dirpath=""
417+ 
418+ eval "${_outvar}=\"\""
419+ 
420+ get_package_dirpath "_package_dirpath" "${_package}"
421+ 
422+ eval "${_outvar}=\"${_install_path}/${_version_dir}/${_package_dirpath}/script\""
423+}
424+ 
425+# 检查参数不为空
426+check_param_not_empty() {
427+ local name="$1"
428+ local error_msg="$2"
429+ local value
430+ 
431+ eval "value=\"\${${name}}\""
432+ 
433+ if [ "${value}" = "" ]; then
434+ comm_log "ERROR" "$2"
435+ return 1
436+ fi
437+ 
438+ return 0
439+}
440+ 
441+# 检查文件存在
442+check_file_exists() {
443+ local path="$1"
444+ local error_msg="$2"
445+ 
446+ if [ ! -f "${path}" ]; then
447+ comm_log "ERROR" "$2"
448+ return 1
449+ fi
450+ 
451+ return 0
452+}
453+ 
454+# 检查返回值是否为0
455+check_ret_error() {
456+ local ret="$1"
457+ local msg="$2"
458+ 
459+ if [ ${ret} -ne 0 ]; then
460+ comm_log "ERROR" "${msg}"
461+ return ${ret}
462+ fi
463+ 
464+ return 0
465+}
466+ 
467+# 检查返回值是否为0
468+check_ret_warning() {
469+ local ret="$1"
470+ local msg="$2"
471+ 
472+ if [ ${ret} -ne 0 ]; then
473+ comm_log "WARNING" "${msg}"
474+ return ${ret}
475+ fi
476+ 
477+ return 0
478+}
479+ 
480+# 获取真实路径
481+get_realpath() {
482+ local _outvar="$1"
483+ local _path_gr="$2"
484+ 
485+ _path_gr="$(readlink -f "${_path_gr}")"
486+ eval "${_outvar}=\"${_path_gr}\""
487+}
488+ 
489+# 检查返回值是否为0
490+cleanup_if_error() {
491+ local ret="$1"
492+ local cleanup="$2"
493+ 
494+ if [ ${ret} -ne 0 ]; then
495+ eval "${cleanup}"
496+ return ${ret}
497+ fi
498+ 
499+ return 0
500+}
501+ 
502+# 获取临时目录
503+get_tmp_root() {
504+ local _outvar="$1"
505+ local _result_gtr
506+ 
507+ if [ -d "${HOME}" ]; then
508+ _result_gtr="${HOME}"
509+ elif [ $(id -u) -eq 0 ] && [ -d "/root" ]; then
510+ _result_gtr="/root"
511+ else
512+ _result_gtr="${PWD}"
513+ fi
514+ 
515+ eval "${_outvar}=\"${_result_gtr}\""
516+ return 0
517+}
518+ 
519+# 获取临时文件
520+get_tmp_file() {
521+ local _outvar="$1"
522+ local _filename="$2"
523+ local _tmp_file_gtf _result
524+ 
525+ get_tmp_root "_tmp_file_gtf"
526+ 
527+ _result=$(mktemp "$_tmp_file_gtf/${_filename}_XXXXXX")
528+ check_ret_warning "$?" "mktemp $_tmp_file_gtf/${_filename}_XXXXXX failed."
529+ ret="$?" && [ $ret -ne 0 ] && return $ret
530+ 
531+ eval "${_outvar}=\"${_result}\""
532+ return 0
533+}
534+ 
535+# 获取包架构
536+get_scene_arch() {
537+ local _outvar="$1"
538+ local _scene_filepath="$2"
539+ local _result
540+ 
541+ _result="$(grep "^arch=" "${_scene_filepath}" | cut -d= -f2-)"
542+ eval "${_outvar}=\"${_result}\""
543+}
544+ 
545+# 打包feature参数
546+pack_feature_param() {
547+ local _outvar="$1"
548+ local _feature_type="$2"
549+ local _feature_exclude_all="$3"
550+ local _chip="$4"
551+ 
552+ eval "${_outvar}=\"${_feature_type} ${_feature_exclude_all} ${_chip}\""
553+}
554+ 
555+# 解包feature参数
556+# 注意,调用解包时参数不可加引号
557+unpack_feature_param() {
558+ local _feature_type_var="$1"
559+ local _feature_exclude_all_var="$2"
560+ local _chip_var="$3"
561+ shift 3
562+ eval "${_feature_type_var}=\"${1}\""
563+ eval "${_feature_exclude_all_var}=\"${2}\""
564+ eval "${_chip_var}=\"${3}\""
565+}
566+ 
567+# 展开version.info文件中参数
568+expand_version_file() {
569+ if [ "${VERSION_FILE}" = "" ]; then
570+ return 0
571+ fi
572+ get_version "VERSION" "${VERSION_FILE}"
573+ get_version_dir "VERSION_DIR" "${VERSION_FILE}"
574+}
575+ 
576+# 提取第一项
577+extract_1st() {
578+ local _outvar="$1"
579+ eval "${_outvar}=\"$2\""
580+}
581+ 
582+# 提取第二项
583+extract_2nd() {
584+ local _outvar="$1"
585+ eval "${_outvar}=\"$3\""
586+}
587+ 
588+# 路径转为sed正则表达式
589+path_to_regex() {
590+ local _outvar="$1"
591+ local _path="$2"
592+ local _reslut_ptr="$(echo "${_path}" | sed "s#\/#\\\/#g")"
593+ 
594+ eval "${_outvar}=\"${_reslut_ptr}\""
595+}
596+ 
597+# 设置默认值
598+set_default() {
599+ local _outvar="$1"
600+ local _input="$2"
601+ local _default="$3"
602+ 
603+ if [ "${_input}" = "" ]; then
604+ eval "${_outvar}=\"${_default}\""
605+ else
606+ eval "${_outvar}=\"${_input}\""
607+ fi
608+}
609+ 
610+# 标准化feature参数
611+# 输入的feature参数中,如果有all字段(表示安装所有feature)
612+# 则将feature参数重置为all,在后续流程中安装所有feature
613+# 卸载流程中,feature与chip强制为all,保证卸载掉block中的所有文件
614+normalize_feature() {
615+ local _outvar="$1"
616+ local _feature_nf="$2"
617+ local _operation="$3"
618+ 
619+ if [ "$_operation" = "uninstall" ]; then
620+ eval "${_outvar}=\"all\""
621+ return 0
622+ fi
623+ 
624+ if [ "$_feature_nf" = "" ]; then
625+ eval "${_outvar}=\"all\""
626+ return 0
627+ fi
628+ 
629+ case "${_feature_nf}" in
630+ all,*)
631+ _feature_nf="all"
632+ ;;
633+ *,all)
634+ _feature_nf="all"
635+ ;;
636+ *,all,*)
637+ _feature_nf="all"
638+ ;;
639+ esac
640+ 
641+ eval "${_outvar}=\"${_feature_nf}\""
642+}
643+ 
644+# 删除软连接
645+remove_softlink_icp() {
646+ local softlink="$1"
647+ 
648+ if [ "${softlink}" = "" ]; then
649+ return 0
650+ fi
651+ 
652+ if [ "${softlink}" != "NA" ] && [ -L "${softlink}" ]; then
653+ rm -f "${softlink}"
654+ if [ $? -ne 0 ]; then
655+ log "ERROR" "remove ${softlink} failed!"
656+ return 1
657+ fi
658+ fi
659+ return 0
660+}
661+ 
662+#移除文件
663+remove_file() {
664+ local target="$1"
665+ local softlink="$2"
666+ local ret
667+ if [ -e "${target}" ] || [ -L "${target}" ]; then
668+ rm -f "${target}"
669+ if [ $? -ne 0 ]; then
670+ log "ERROR" "remove ${target} failed!"
671+ return 1
672+ fi
673+ fi
674+ remove_softlink_icp "${softlink}"
675+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
676+ 
677+ return 0
678+}
679+ 
680+# 创建文件夹
681+create_folder() {
682+ local install_path="$1"
683+ local target="$2"
684+ local softlinks_str="$3"
685+ local ret target_abs
686+ 
687+ __set_abs_path "${install_path}" "${target}" "target_abs"
688+ 
689+ if [ ! -d "${target_abs}" ]; then
690+ make_dir "${target_abs}"
691+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
692+ fi
693+ change_mod "${target_abs}" "${RESET_MOD}" ""
694+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
695+ if [ "${softlinks_str}" != "NA" ]; then
696+ create_softlink_by_install_path "${install_path}" "${target}" "${softlinks_str}"
697+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
698+ fi
699+ return 0
700+}
701+ 
702+# 创建目录
703+create_dirs() {
704+ local install_path="$1"
705+ local line="$2"
706+ local target
707+ local target_abs
708+ local softlinks_str
709+ local ret
710+ 
711+ __index_list "${line}" 1 "target" 4 "softlinks_str"
712+ 
713+ __set_abs_path "${install_path}" "${target}" "target_abs"
714+ 
715+ if [ -L "${target_abs}" ] ; then
716+ rm -f "${target_abs}"
717+ log "WARNING" "${target_abs} is an existing soft-link, deleted."
718+ fi
719+ create_folder "${install_path}" "${target}" "${softlinks_str}"
720+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
721+ return 0
722+}
723+ 
724+# 修改目录的权限和属组
725+reset_mod_dirs() {
726+ local install_path="$1"
727+ local line="$2"
728+ local mod
729+ local target
730+ local target_abs
731+ local is_abs_path ret
732+ 
733+ __index_list "${line}" 1 "target"
734+ 
735+ __set_abs_path "${install_path}" "${target}" "target_abs"
736+ 
737+ # 目录不存在时跳过
738+ if [ ! -d "${target_abs}" ]; then
739+ return 0
740+ fi
741+ # 只处理目录,没有处理目录的软链接
742+ change_mod "${target_abs}" "${RESET_MOD}" "" "false"
743+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
744+ 
745+ return 0
746+}
747+ 
748+# 递归修改目录的权限和属组
749+reset_mod_dirs_recursive() {
750+ local install_path="$1"
751+ local line="$2"
752+ local mod
753+ local target
754+ local target_abs
755+ local is_abs_path ret
756+ 
757+ __index_list "${line}" 1 "target"
758+ 
759+ __set_abs_path "${install_path}" "${target}" "target_abs"
760+ 
761+ # 目录不存在时跳过
762+ if [ ! -d "${target_abs}" ]; then
763+ return 0
764+ fi
765+ # 只处理目录,没有处理目录的软链接
766+ change_mod "${target_abs}" "${RESET_MOD}" "" "true"
767+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
768+ 
769+ return 0
770+}
771+ 
772+__unpack_softlinks() {
773+ local softlinks_str="$1"
774+ local varname="$2"
775+ local item
776+ local temp
777+ OLD_IFS="${IFS}"
778+ IFS=";"
779+ temp=""
780+ for item in ${softlinks_str}; do
781+ if [ "${temp}" = "" ]; then
782+ temp="${item}"
783+ else
784+ temp="${temp} ${item}"
785+ fi
786+ done
787+ IFS="${OLD_IFS}"
788+ 
789+ eval "${varname}=\"${temp}\""
790+}
791+ 
792+#移除文件夹
793+remove_dir_icp() {
794+ if [ -e "$1" ] || [ -L "$1" ]; then
795+ rm -fr "$1"
796+ if [ $? -ne 0 ]; then
797+ log "ERROR" "$1 remove failed!"
798+ return 1
799+ fi
800+ fi
801+ return 0
802+}
803+ 
804+# 创建软链时,移除存在的目录
805+remove_exists_dir_in_create_softlink() {
806+ local dirpath="$1"
807+ 
808+ if [ -d "${dirpath}" ] && [ ! -L "${dirpath}" ]; then
809+ log "WARNING" "${dirpath} is an existing directory in create softlink, deleted."
810+ change_mod "${dirpath}" "700" "n" "true"
811+ remove_dir_icp "${dirpath}"
812+ fi
813+}
814+ 
815+# 创建绝对软链接
816+create_softlink_icp_absolute() {
817+ local src_path="$1"
818+ local dst_path="$2"
819+ 
820+ remove_exists_dir_in_create_softlink "${dst_path}"
821+ ln -sfn "${src_path}" "${dst_path}"
822+ if [ $? -ne 0 ]; then
823+ log "ERROR" "create softlink absolute from ${src_path} to ${dst_path} failed!"
824+ return 1
825+ fi
826+ return 0
827+}
828+ 
829+# 该函数与common_func.inc脚本中的相同
830+# install_common_parser.sh不一定能source到common_func.inc(原因见source common_func.inc的注释)
831+# 所以这里需要重复定义
832+create_softlink_icp_relative() {
833+ local src_path="$1"
834+ local dst_path="$2"
835+ local source="top${src_path}"
836+ local target="top${dst_path}"
837+ 
838+ # 若变量内容从尾向前的数据符合,则将符合的最短数据删除
839+ local common="${target%/*}"
840+ # 若变量内容从头开始的数据符合,则将符合的最短数据删除
841+ local forward="${source#"$common"/}"
842+ 
843+ local result=""
844+ 
845+ while [ "${forward}" = "${source}" ]; do
846+ common="$(dirname "$common")"
847+ forward="${source#"$common"/}"
848+ result="../${result}"
849+ done
850+ 
851+ result="${result}${forward}"
852+ 
853+ remove_exists_dir_in_create_softlink "${dst_path}"
854+ ln -sfn "${result}" "${dst_path}"
855+ if [ $? -ne 0 ]; then
856+ log "ERROR" "create softlink relative from ${src_path} to ${dst_path} failed!"
857+ return 1
858+ fi
859+ return 0
860+}
861+ 
862+# 创建软连接
863+create_softlink_by_install_path() {
864+ local install_path="$1"
865+ local target="$2"
866+ local softlinks_str="$3"
867+ local softlinks softlink
868+ local target_abs
869+ local softlink_abs
870+ local is_abs_path
871+ local ret
872+ 
873+ if [ "${softlinks_str}" = "NA" ]; then
874+ return 0
875+ fi
876+ 
877+ __unpack_softlinks "${softlinks_str}" "softlinks"
878+ 
879+ for softlink in ${softlinks}; do
880+ __check_abs_path "${target}"
881+ if [ "${is_abs_path}" = "true" ]; then
882+ __set_abs_path "${install_path}" "${softlink}" "softlink_abs"
883+ create_softlink_icp_absolute "${target}" "${softlink_abs}"
884+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
885+ continue
886+ fi
887+ 
888+ __check_abs_path "${softlink}"
889+ if [ "${is_abs_path}" = "true" ]; then
890+ __set_abs_path "${install_path}" "${target}" "target_abs"
891+ create_softlink_icp_absolute "${target_abs}" "${softlink}"
892+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
893+ continue
894+ fi
895+ 
896+ create_softlink_icp_relative "${install_path}/${target}" "${install_path}/${softlink}"
897+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
898+ done
899+ 
900+ return 0
901+}
902+ 
903+# 创建软链接。icp后缀为避免重名
904+create_softlink_icp() {
905+ local options=""
906+ local is_relative="false"
907+ 
908+ while true; do
909+ case "$1" in
910+ -r|--relative)
911+ is_relative="true"
912+ shift
913+ ;;
914+ -*)
915+ log "ERROR" "unsupported option $1 in create softlink icp!"
916+ return 1
917+ ;;
918+ *)
919+ break
920+ ;;
921+ esac
922+ done
923+ 
924+ local src_path="$1"
925+ local dst_path="$2"
926+ 
927+ if [ "${is_relative}" = "true" ]; then
928+ create_softlink_icp_relative "${src_path}" "${dst_path}"
929+ else
930+ create_softlink_icp_absolute "${src_path}" "${dst_path}"
931+ fi
932+}
933+ 
934+# 修改权限和属组
935+change_mod_and_own(){
936+ local target="$1"
937+ local mod="$2"
938+ local own="$3"
939+ local install_for_all="$4"
940+ local recursive="$5"
941+ local ret
942+ 
943+ change_mod "${target}" "${mod}" "${install_for_all}" "${recursive}"
944+ ret="$?" && [ $ret -ne 0 ] && return $ret
945+ 
946+ change_own "${target}" "${own}" "${recursive}"
947+ ret="$?" && [ $ret -ne 0 ] && return $ret
948+ 
949+ return 0
950+}
951+ 
952+# 修改目录的权限和属组
953+change_mod_and_own_dirs() {
954+ local install_path="$1"
955+ local line="$2"
956+ local target
957+ local mod
958+ local own
959+ local is_abs_path
960+ 
961+ __index_list "${line}" 1 "target" 2 "mod" 3 "own"
962+ 
963+ __check_abs_path "${target}"
964+ if [ "${is_abs_path}" != "true" ]; then
965+ target="${install_path}/${target}"
966+ fi
967+ if [ ! -d "${target}" ]; then
968+ return 0
969+ fi
970+ # 只处理目录,没有处理目录的软链接
971+ change_mod_and_own "${target}" "${mod}" "${own}" "${INSTALL_FOR_ALL}" "false"
972+}
973+ 
974+# 创建stash_mod.txt文件
975+create_stash_mod() {
976+ local install_path="$1"
977+ rm -f "${install_path}/${STASH_MOD_PATH}"
978+ touch "${install_path}/${STASH_MOD_PATH}"
979+ chmod ${STASH_FILE_MOD} "${install_path}/${STASH_MOD_PATH}"
980+}
981+ 
982+# 删除stash_mod.txt文件
983+remove_stash_mod() {
984+ local install_path="$1"
985+ rm -f "${install_path}/${STASH_MOD_PATH}"
986+}
987+ 
988+# 修改目录的权限和属组
989+restore_stash_mod() {
990+ local install_path="$1"
991+ local line="$2"
992+ local target
993+ local target_abs
994+ local mod
995+ local is_abs_path ret
996+ 
997+ __index_list "${line}" 0 "target" 1 "mod"
998+ 
999+ __set_abs_path "${install_path}" "${target}" "target_abs"
1000+ 
1001+ # 目录不存在时跳过
1002+ if [ ! -d "${target_abs}" ]; then
1003+ return 0
1004+ fi
1005+ # 只处理目录,没有处理目录的软链接
1006+ change_mod "${target_abs}" "${mod}" "${INSTALL_FOR_ALL}"
1007+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
1008+ 
1009+ return 0
1010+}
1011+ 
1012+# 修改目录的权限,暂存原文件权限
1013+reset_mod_dirs_with_stash_mod() {
1014+ local install_path="$1"
1015+ local line="$2"
1016+ local mod
1017+ local target
1018+ local target_abs
1019+ local is_abs_path ret
1020+ 
1021+ __index_list "${line}" 1 "target"
1022+ 
1023+ __set_abs_path "${install_path}" "${target}" "target_abs"
1024+ 
1025+ # 目录不存在时跳过
1026+ if [ ! -d "${target_abs}" ]; then
1027+ return 0
1028+ fi
1029+ 
1030+ get_file_mod "mod" "${target_abs}"
1031+ echo "${target}:${mod}" >> "${install_path}/${STASH_MOD_PATH}"
1032+ 
1033+ # 只处理目录,没有处理目录的软链接
1034+ change_mod "${target_abs}" "${RESET_MOD}" ""
1035+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
1036+ 
1037+ return 0
1038+}
1039+ 
1040+# 删除软连接列表
1041+remove_softlinks() {
1042+ local install_path="$1"
1043+ local softlinks_str="$2"
1044+ local softlinks softlink softlink_abs ret
1045+ 
1046+ __unpack_softlinks "${softlinks_str}" "softlinks"
1047+ 
1048+ for softlink in ${softlinks}; do
1049+ __set_abs_path "${install_path}" "${softlink}" "softlink_abs"
1050+ remove_softlink_icp "${softlink_abs}"
1051+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
1052+ done
1053+ return 0
1054+}
1055+ 
1056+# 删除安装文件夹
1057+remove_install_dirs() {
1058+ local install_path="$1"
1059+ local line="$2"
1060+ local target softlinks_str is_abs_path
1061+ 
1062+ __index_list "${line}" 1 "target" 4 "softlinks_str"
1063+ 
1064+ if [ "${target}" != "NA" ]; then
1065+ __check_abs_path "${target}"
1066+ if [ "${is_abs_path}" != "true" ]; then
1067+ target="${install_path}/${target}"
1068+ fi
1069+ if [ -d "${target}" ]; then
1070+ # 不同的blocks中,可能配置相同的dir_info。目录不为空时不删除。
1071+ if [ "$(ls -A "${target}")" != "" ]; then
1072+ return 0
1073+ fi
1074+ remove_dir_icp "${target}"
1075+ ret="$?" && [ $ret -ne 0 ] && return $ret
1076+ fi
1077+ 
1078+ if [ "${softlinks_str}" != "NA" ]; then
1079+ remove_softlinks "${install_path}" "${softlinks_str}"
1080+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
1081+ fi
1082+ fi
1083+ return 0
1084+}
1085+ 
1086+# 获取所有的blocks
1087+get_blocks_info() {
1088+ local _outvar="$1"
1089+ local _install_path="$2"
1090+ local _result _db_filepath="${_install_path}/${PKG_DB_INFO_RELPATH}"
1091+ 
1092+ if [ ! -f "${_db_filepath}" ]; then
1093+ return 0
1094+ fi
1095+ _result="$(cut -d'|' -f1 "${_db_filepath}" | xargs)"
1096+ eval "${_outvar}=\"${_result}\""
1097+}
1098+ 
1099+# 列表转为正则表达式
1100+trans_list_to_regex() {
1101+ local _outvar="$1"
1102+ local _value="$2"
1103+ # 移除右侧空白符,mawk无法编译右侧存在空白符时的正则表达式,如:^(EngineeringCommon|)$
1104+ local _result="$(echo "$_value" | sed 's/\s\+$//g' | sed 's/ /|/g' | xargs printf "^(%s)$")"
1105+ eval "${_outvar}=\"${_result}\""
1106+}
1107+ 
1108+parse_filelist_core() {
1109+ awk -F, '{ print $3,$4,$6,$7,$9,$12,$13,$14,$15 }'
1110+}
1111+ 
1112+# 过滤公共路径条目
1113+filter_common_dirs() {
1114+ awk -F, '
1115+ $11 == "Y" {
1116+ print $0
1117+ }
1118+ $11 == "YY" {
1119+ print $1 "," $2 "," $3 "," $4 "," $5 "," $6 "," $7 "," $8 ",NA," $10 "," $11 "," $12 "," $13 "," $14 "," $15 "," $16
1120+ }
1121+ '
1122+}
1123+ 
1124+# 过滤包内软链
1125+filter_pkg_inner_softlink() {
1126+ awk -F, '$15 != "NA" { print $0 }'
1127+}
1128+ 
1129+# 过滤块
1130+filter_blocks() {
1131+ local blocks="$1"
1132+ local blocks_reg
1133+ 
1134+ trans_list_to_regex "blocks_reg" "$blocks"
1135+ awk -F, "\$14 ~ \"$blocks_reg\" {print \$0}"
1136+}
1137+ 
1138+# 过滤不匹配块
1139+filterfalse_blocks() {
1140+ local blocks="$1"
1141+ local blocks_reg
1142+ 
1143+ trans_list_to_regex "blocks_reg" "$blocks"
1144+ awk -F, "\$14 !~ \"$blocks_reg\" {print \$0}"
1145+}
1146+ 
1147+# 过滤操作类型条目
1148+filter_operate_type() {
1149+ local operator_type="$1"
1150+ local operator_type_reg
1151+ 
1152+ trans_list_to_regex "operator_type_reg" "$operator_type"
1153+ 
1154+ # ~为部分匹配,正则表达式需要匹配头尾
1155+ awk -F, "\$2 ~ \"$operator_type_reg\" {print \$0}"
1156+}
1157+ 
1158+# 过滤安装类型条目
1159+filter_install_type() {
1160+ local install_type="$1"
1161+ local install_type_reg
1162+ 
1163+ # 注意:特征串不能嵌套,否则会错误匹配
1164+ if [ "$install_type" != "full" ] && [ "$install_type" != "debug" ]; then
1165+ install_type_reg="(all)|($install_type)"
1166+ else
1167+ install_type_reg="(all)|(docker)|(devel)|(run)"
1168+ fi
1169+ 
1170+ awk -F, "\$8 ~ \"$install_type_reg\" {print \$0}"
1171+}
1172+ 
1173+# 过滤特性参数
1174+filter_feature_param() {
1175+ local feature_param="$1"
1176+ local feature_type
1177+ local feature_exclude_all # feature是否为排除公共all文件
1178+ local chip chip_list # 芯片类型
1179+ local feature_list feature_type_list
1180+ 
1181+ unpack_feature_param "feature_type" "feature_exclude_all" "chip" ${feature_param}
1182+ 
1183+ if [ "$feature_type" != "all" ]; then
1184+ feature_list="$(echo $feature_type | tr ',' ' ')"
1185+ if [ "${feature_exclude_all}" = "y" ]; then
1186+ feature_type_list="${feature_list}"
1187+ else
1188+ feature_type_list="comm ${feature_list}"
1189+ fi
1190+ else
1191+ feature_type_list="all"
1192+ fi
1193+ 
1194+ if [ "$chip" != "all" ]; then
1195+ chip_list="all $(echo $chip | tr ',' ' ')"
1196+ else
1197+ chip_list="all"
1198+ fi
1199+ 
1200+ # filelist中的feature为all,表示所有场景下都安装这个文件
1201+ # 输入参数中的feature为all,表示安装所有的feature
1202+ awk -v feature_type_list="${feature_type_list}" \
1203+ -v chip_list="${chip_list}" '
1204+ BEGIN {
1205+ FS= ","
1206+ split(feature_type_list, input_feature_type_arr, " ")
1207+ split(chip_list, input_chip_arr, " ")
1208+ }
1209+ 
1210+ function match_feature_type(features_str) {
1211+ if (feature_type_list == "all") {
1212+ return 1
1213+ }
1214+ matched_feature_type_tmp=0
1215+ split(features_str, features, ";")
1216+ for(i in features)
1217+ {
1218+ for(j in input_feature_type_arr) {
1219+ if(input_feature_type_arr[j] == features[i]) {
1220+ matched_feature_type_tmp = 1
1221+ break;
1222+ }
1223+ }
1224+ }
1225+ return matched_feature_type_tmp
1226+ }
1227+ 
1228+ function match_chip(chip_str) {
1229+ if (chip_list == "all") {
1230+ return 1
1231+ }
1232+ matched_chip_tmp=0
1233+ split(chip_str, chips, ";")
1234+ for(i in chips)
1235+ {
1236+ for(j in input_chip_arr) {
1237+ if(input_chip_arr[j] == chips[i]) {
1238+ matched_chip_tmp = 1
1239+ break;
1240+ }
1241+ }
1242+ }
1243+ return matched_chip_tmp
1244+ }
1245+ 
1246+ {
1247+ matched_feature_type = match_feature_type($10);
1248+ if (matched_feature_type == 0) next;
1249+ 
1250+ matched_chip = match_chip($16)
1251+ if (matched_chip == 0) next
1252+ 
1253+ print $0
1254+ }'
1255+}
1256+ 
1257+# 读取fileist的第2行到最后行
1258+tail_filelist() {
1259+ local filelist_path="$1"
1260+ tail -n +2 "$filelist_path"
1261+}
1262+ 
1263+# 解析filelist.csv脚本
1264+parse_filelist() {
1265+ local install_type="$1"
1266+ local operate_type="$2"
1267+ local filelist_path="$3"
1268+ local feature_param="$4"
1269+ local filter_type="$5"
1270+ local blocks="$6"
1271+ 
1272+ if [ ! -f "$filelist_path" ]; then
1273+ log "ERROR" "filelist $filelist_path does not exist!"
1274+ exit 1
1275+ fi
1276+ 
1277+ # 注意:这里的filelist需要是全局变量。其它函数(如add_filelist_blocks_info)会引用这个变量。
1278+ filelist=$(
1279+ parse_filelist_v2 "$install_type" "$operate_type" "$filelist_path" "$feature_param" \
1280+ "$filter_type" "$blocks"
1281+ )
1282+}
1283+ 
1284+# 解析filelist.csv脚本
1285+parse_filelist_v2() {
1286+ local install_type="$1"
1287+ local operate_type="$2"
1288+ local filelist_path="$3"
1289+ local feature_param="$4"
1290+ local filter_type="$5"
1291+ local blocks="$6"
1292+ local filter_cmds=""
1293+ 
1294+ if [ "$operate_type" != "all" ]; then
1295+ filter_cmds="$filter_cmds | filter_operate_type \"$operate_type\""
1296+ fi
1297+ if printf "%s" "$filter_type" | grep -Eq "\<filter_common_dirs\>"; then
1298+ filter_cmds="$filter_cmds | filter_common_dirs"
1299+ fi
1300+ if printf "%s" "$filter_type" | grep -Eq "\<filter_by_pkg_inner_softlink\>"; then
1301+ filter_cmds="$filter_cmds | filter_pkg_inner_softlink"
1302+ fi
1303+ if printf "%s" "$filter_type" | grep -Eq "\<filter_by_blocks\>|\<filterfalse_blocks\>" && [ "$blocks" != "" ]; then
1304+ filter_cmds="$filter_cmds | filterfalse_blocks \"$blocks\""
1305+ fi
1306+ if printf "%s" "$filter_type" | grep -Eq "\<filter_blocks\>" && [ "$blocks" != "" ]; then
1307+ filter_cmds="$filter_cmds | filter_blocks \"$blocks\""
1308+ fi
1309+ 
1310+ tail_filelist "$filelist_path" \
1311+ | filter_install_type "$install_type" \
1312+ | filter_feature_param "$feature_param" \
1313+ | eval cat $filter_cmds \
1314+ | parse_filelist_core
1315+}
1316+ 
1317+ 
1318+pack_exec_params() {
1319+ local _outvar="$1"
1320+ local _exec_mode="$2"
1321+ 
1322+ eval "${_outvar}=\"${_exec_mode}\""
1323+}
1324+ 
1325+ 
1326+# 迭代filelist中的条目,执行操作
1327+foreach_filelist_exec() {
1328+ local filelist="$1"
1329+ local sort_filelist="$2"
1330+ local exec_mode="$3"
1331+ local exec_func="$4"
1332+ local install_path="$5"
1333+ local ret=0 tmp_ret exec_params
1334+ shift 5
1335+ 
1336+ pack_exec_params "exec_params" "${exec_mode}"
1337+ 
1338+ if [ "${sort_filelist}" = "reverse" ]; then
1339+ # 对第二列文件路径做倒序排列,保证先删除子文件夹,再删除父文件夹
1340+ # 不可以使用echo "${filelist}"
1341+ # 在dash中会消耗\\$username:\\$usergroup中的一个反斜线(bash与busybox的sh无此问题)
1342+ # 需要使用here document,防止shell的解析过程
1343+ filelist=$(sort -k2,2 -b -r <<EOF
1344+${filelist}
1345+EOF
1346+ )
1347+ elif [ "${sort_filelist}" = "no" ]; then
1348+ # 不排序
1349+ :
1350+ else
1351+ log "ERROR" "sort_filelist param wrong! sort_filelist is ${sort_filelist}, exec_func is ${exec_func}, parse_func is ${parse_func}"
1352+ exit 1
1353+ fi
1354+ 
1355+ if [ "${exec_mode}" = "concurrency" ] || [ "${exec_mode}" = "normal" ]; then
1356+ # 并发模式或正常模式
1357+ :
1358+ else
1359+ log "ERROR" "exec_mode param wrong! exec_mode is ${exec_mode}, exec_func is ${exec_func}, parse_func is ${parse_func}"
1360+ exit 1
1361+ fi
1362+ 
1363+ while read line; do
1364+ array=${line}
1365+ __length_list "${array}" "len_array"
1366+ if [ ${len_array} -eq 0 ]; then
1367+ continue
1368+ fi
1369+ "${exec_func}" "${install_path}" "${line}" "${exec_params}" "$@"
1370+ tmp_ret="$?" && [ ${tmp_ret} -ne 0 ] && ret="${tmp_ret}"
1371+ done << EOF
1372+${filelist}
1373+EOF
1374+ 
1375+ if [ "${exec_mode}" = "concurrency" ]; then
1376+ wait
1377+ fi
1378+ 
1379+ return $ret
1380+}
1381+ 
1382+# 迭代遍历filelist中的条目,执行一些操作
1383+foreach_filelist() {
1384+ local filter_type="$1"
1385+ local exec_func="$2"
1386+ local install_type="$3"
1387+ local install_path="$4"
1388+ local operate_type="$5"
1389+ local filelist_path="$6"
1390+ local feature_param="$7"
1391+ local sort_filelist="$8" # 排序filelist,可选参数为[no,reverse]
1392+ local exec_mode="$9" # 执行模式,可选参数为[normal,concurrency]
1393+ shift 9
1394+ local ret blocks
1395+ 
1396+ if [ "$filter_type" = "filter_by_blocks" ]; then
1397+ get_blocks_info "blocks" "$install_path"
1398+ fi
1399+ 
1400+ foreach_filelist_v2 "$exec_func" "$install_type" "$install_path" "$operate_type" "$filelist_path" \
1401+ "$feature_param" "$filter_type" "$blocks" "$sort_filelist" "$exec_mode" "$@"
1402+ ret="$?" && [ $ret -ne 0 ] && return $ret
1403+ 
1404+ return 0
1405+}
1406+ 
1407+# 迭代遍历filelist中的条目,执行一些操作
1408+foreach_filelist_v2() {
1409+ local exec_func="$1"
1410+ local install_type="$2"
1411+ local install_path="$3"
1412+ local operate_type="$4"
1413+ local filelist_path="$5"
1414+ local feature_param="$6"
1415+ local filter_type="$7"
1416+ local blocks="$8"
1417+ local sort_filelist="$9" # 排序filelist,可选参数为[no,reverse]
1418+ local exec_mode="${10}" # 执行模式,可选参数为[normal,concurrency]
1419+ shift 10
1420+ local filter_cmds=""
1421+ local ret
1422+ 
1423+ parse_filelist "${install_type}" "${operate_type}" "${filelist_path}" "${feature_param}" \
1424+ "${filter_type}" "${blocks}"
1425+ 
1426+ foreach_filelist_exec "${filelist}" "${sort_filelist}" "${exec_mode}" "${exec_func}" "${install_path}" "$@"
1427+ ret="$?" && [ $ret -ne 0 ] && return $ret
1428+ 
1429+ return 0
1430+}
1431+ 
1432+# 解析stash_mod.txt
1433+parse_stashmod() {
1434+ local stashmod_path="$1"
1435+ 
1436+ if [ ! -f "${stashmod_path}" ]; then
1437+ log "ERROR" "stash mod ${stashmod_path} does not exist!"
1438+ exit 1
1439+ fi
1440+ 
1441+ stashmod_list=$(awk '
1442+ BEGIN{
1443+ FS=":"
1444+ }
1445+ {
1446+ print $1,$2
1447+ }' "${stashmod_path}")
1448+}
1449+ 
1450+# 迭代遍历stash_mod中的条目,执行操作
1451+foreach_stashmod() {
1452+ local exec_func="$1"
1453+ local install_path="$2"
1454+ local sort_type="$3" # 排序方式,可选参数为[no,reverse]
1455+ local array
1456+ local len_array
1457+ local line
1458+ local ret=0 tmp_ret
1459+ 
1460+ parse_stashmod "${install_path}/${STASH_MOD_PATH}"
1461+ 
1462+ if [ "${sort_type}" = "reverse" ]; then
1463+ # 对第二列文件路径做倒序排列,保证先处理子文件夹,再处理父文件夹
1464+ stashmod_list=$(echo "${stashmod_list}" | sort -k1,1 -r)
1465+ elif [ "${sort_type}" = "no" ]; then
1466+ # 不排序
1467+ echo > /dev/null
1468+ else
1469+ log "ERROR" "sort_type param wrong! sort_type is ${sort_type}, exec_func is ${exec_func}"
1470+ exit 1
1471+ fi
1472+ 
1473+ while read line; do
1474+ array=${line}
1475+ __length_list "${array}" "len_array"
1476+ if [ ${len_array} -eq 0 ]; then
1477+ continue
1478+ fi
1479+ "${exec_func}" "${install_path}" "${line}"
1480+ tmp_ret="$?" && [ ${tmp_ret} -ne 0 ] && ret="${tmp_ret}"
1481+ done << EOF
1482+${stashmod_list}
1483+EOF
1484+ 
1485+ return $ret
1486+}
1487+ 
1488+# 创建目录并且设置权限
1489+make_dir_with_permission() {
1490+ local path="$1"
1491+ local mod="$2"
1492+ local username="$3"
1493+ local usergroup="$4"
1494+ local install_for_all="$5"
1495+ local ret
1496+ 
1497+ make_dir "${path}"
1498+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
1499+ 
1500+ change_mod "${path}" "${mod}" "${install_for_all}"
1501+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
1502+ 
1503+ change_own "${path}" "${username}:${usergroup}"
1504+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
1505+ 
1506+ return 0
1507+}
1508+ 
1509+# blocks转换为db.info配置
1510+blocks_to_db_item() {
1511+ local package="$1"
1512+ local version_dir="$2"
1513+ 
1514+ xargs printf "%s|[$package:$version_dir]\n"
1515+}
1516+ 
1517+# 折叠第2个参数
1518+# 需要保证输入数据第1个参数已排序
1519+fold_2() {
1520+ local fs="$1"
1521+ local concat="$2"
1522+ local options=""
1523+ 
1524+ if [ "$fs" != "" ]; then
1525+ options="-F$fs"
1526+ fi
1527+ 
1528+ awk $options -v CONCAT="$concat" '
1529+ NR == 1 {
1530+ field1 = $1
1531+ field2 = $2
1532+ }
1533+ NR != 1 {
1534+ if (field1 == $1) {
1535+ field2 = field2 CONCAT $2
1536+ } else {
1537+ print field1 FS field2
1538+ field1 = $1
1539+ field2 = $2
1540+ }
1541+ }
1542+ END {
1543+ if (field1) {
1544+ print field1 FS field2
1545+ }
1546+ }'
1547+}
1548+ 
1549+# 折叠第3个参数,保持第2个参数
1550+# 需要保证输入数据第1个参数已排序
1551+fold_3_keep_2() {
1552+ local fs="$1"
1553+ local concat="$2"
1554+ local options=""
1555+ 
1556+ if [ "$fs" != "" ]; then
1557+ options="-F$fs"
1558+ fi
1559+ 
1560+ awk $options -v CONCAT="$concat" '
1561+ NR == 1 {
1562+ field1 = $1
1563+ field2 = $2
1564+ field3 = $3
1565+ }
1566+ NR != 1 {
1567+ if (field1 == $1) {
1568+ field3 = field3 CONCAT $3
1569+ } else {
1570+ print field1 FS field2 FS field3
1571+ field1 = $1
1572+ field2 = $2
1573+ field3 = $3
1574+ }
1575+ }
1576+ END {
1577+ if (field1) {
1578+ print field1 FS field2 FS field3
1579+ }
1580+ }'
1581+}
1582+ 
1583+# 根据第1列排序
1584+sort_1() {
1585+ local sep="$1"
1586+ local options=""
1587+ if [ "$sep" != "" ]; then
1588+ options="-t$sep"
1589+ fi
1590+ sort $options -k1,1 -s
1591+}
1592+ 
1593+# 显示3,4列有差异的条目
1594+show_diff_3_4() {
1595+ awk '$3 != $4 { print $0 }'
1596+}
1597+ 
1598+# 显示具有最少列数的条目
1599+show_min_nf() {
1600+ local min_nf="$1"
1601+ awk -v MIN_NF="$min_nf" 'NF >= MIN_NF { print $0 }'
1602+}
1603+ 
1604+# 选取第3,2,1列
1605+select_fields_3_2_1() {
1606+ awk '{print $3, $2, $1}'
1607+}
1608+ 
1609+# 选取第1列
1610+select_fields_1() {
1611+ awk '{print $1}'
1612+}
1613+ 
1614+# 删除db.info配置
1615+del_db_items() {
1616+ local package="$1"
1617+ local version_dir="$2"
1618+ 
1619+ sed "s/\\[$package:$version_dir\\]//g; /|\$/d"
1620+}
1621+ 
1622+# 保留块最后一个配置
1623+# 输出:第1列(块),第2列(包名),第3列(版本目录)
1624+remain_db_last_item() {
1625+ awk -F '[|:\\[\\]]+' '{ print $1, $(NF-2), $(NF-1) }'
1626+}
1627+ 
1628+# 移除空白行
1629+remove_blank_line() {
1630+ sed '/^$/d'
1631+}
1632+ 
1633+# 保证文件权限
1634+ensure_permission() {
1635+ local path="$1"
1636+ local mod="$2"
1637+ local username="$3"
1638+ local usergroup="$4"
1639+ local install_for_all="$5"
1640+ 
1641+ change_mod "$path" "$mod" "$install_for_all"
1642+ ret=$? && [ $ret -ne 0 ] && return $ret
1643+ 
1644+ change_own "$path" "$username:$usergroup"
1645+ ret=$? && [ $ret -ne 0 ] && return $ret
1646+ 
1647+ return 0
1648+}
1649+ 
1650+# 创建文件
1651+touch_file() {
1652+ local path="$1"
1653+ local mod="$2"
1654+ local username="$3"
1655+ local usergroup="$4"
1656+ local install_for_all="$5"
1657+ local ret
1658+ 
1659+ touch "$path"
1660+ ret=$? && [ $ret -ne 0 ] && return $ret
1661+ 
1662+ ensure_permission "$path" "$mod" "$username" "$usergroup" "$install_for_all"
1663+ ret=$? && [ $ret -ne 0 ] && return $ret
1664+ 
1665+ return 0
1666+}
1667+ 
1668+# 保证文件存在
1669+ensure_file() {
1670+ local path="$1"
1671+ local mod="$2"
1672+ local username="$3"
1673+ local usergroup="$4"
1674+ local install_for_all="$5"
1675+ local ret
1676+ 
1677+ if [ ! -f "$path" ]; then
1678+ touch_file "$path" "$mod" "$username" "$usergroup" "$install_for_all"
1679+ ret=$? && [ $ret -ne 0 ] && return $ret
1680+ fi
1681+ 
1682+ return 0
1683+}
1684+ 
1685+# 写文本文件
1686+write_text() {
1687+ local content="$1"
1688+ local filepath="$2"
1689+ 
1690+ printf "%s\n" "$content" > "$filepath"
1691+}
1692+ 
1693+# 修改文件权限并操作
1694+with_chmod() {
1695+ local path="$1"
1696+ local mod="$2"
1697+ local origin_mod ret
1698+ shift 2
1699+ 
1700+ origin_mod="$(stat -L -c "%a" "$path")"
1701+ chmod "$mod" "$path"
1702+ "$@"
1703+ ret="$?"
1704+ chmod "$origin_mod" "$path"
1705+ 
1706+ return $ret
1707+}
1708+ 
1709+# filelist中所有公共目录的块
1710+all_common_dirs_blocks_in_filelist() {
1711+ local install_type="$1"
1712+ local filelist_path="$2"
1713+ local feature_param="$3"
1714+ 
1715+ parse_filelist_v2 "$install_type" "all" "$filelist_path" "$feature_param" "filter_common_dirs" "" \
1716+ | cut -d' ' -f8 | sort | uniq
1717+}
1718+ 
1719+# 是否为版本目录
1720+is_version_dirpath() {
1721+ local version_dirpath="$1"
1722+ if [ -d "$version_dirpath/share/info" ]; then
1723+ return 0
1724+ fi
1725+ if [ -f "$version_dirpath/var/ascend_package_db.info" ]; then
1726+ return 0
1727+ fi
1728+ return 1
1729+}
Ascripts/package/common/sh/common_installer.inc+55-0
@@ -0,0 +1,55 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+# 通知latest管理器创建版本软链
13+notify_latest_manager_create_version_softlink() {
14+ local curpath install_path version_dir var_path
15+ 
16+ set_comm_log "Notifier"
17+ 
18+ curpath="$(dirname $(readlink -f "${BASH_SOURCE:-$0}"))"
19+ install_path="$(readlink -f "$curpath/..")"
20+ version_dir="$(basename "$curpath")"
21+ var_path="$install_path/$LATEST_DIR/var"
22+ 
23+ if [ ! -f "$var_path/manager.sh" ]; then
24+ comm_log "ERROR" "$var_path/manager.sh doesn't exist!"
25+ exit 2
26+ fi
27+ 
28+ if ! "$var_path/manager.sh" --version-dir "$version_dir" create_version_softlink; then
29+ comm_log "ERROR" "create version softlink failed!"
30+ exit 1
31+ fi
32+ return 0
33+}
34+ 
35+# 通知latest管理器删除latest软链
36+notify_latest_manager_remove_latest_softlink() {
37+ local curpath install_path var_path
38+ 
39+ set_comm_log "Notifier"
40+ 
41+ curpath="$(dirname $(readlink -f "${BASH_SOURCE:-$0}"))"
42+ install_path="$(readlink -f "$curpath/..")"
43+ var_path="$install_path/$LATEST_DIR/var"
44+ 
45+ if [ ! -f "$var_path/manager.sh" ]; then
46+ comm_log "ERROR" "$var_path/manager.sh doesn't exist!"
47+ exit 2
48+ fi
49+ 
50+ if ! "$var_path/manager.sh" remove_latest_softlink; then
51+ comm_log "ERROR" "remove latest softlink failed!"
52+ exit 1
53+ fi
54+ return 0
55+}
Ascripts/package/common/sh/common_interface.bash+61-0
@@ -0,0 +1,61 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+mk_custom_path() {
13+ if [ $(id -u) -eq 0 ]; then
14+ return 0
15+ fi
16+ local _custom_path_file="$1"
17+ while read line || [ -n "$line" ]
18+ do
19+ local _custom_path="$(echo "$line" | cut --only-delimited -d= -f2)"
20+ if [ -z "$_custom_path" ]; then
21+ continue
22+ fi
23+ eval "_custom_path=$_custom_path"
24+ if [ ! -d "$_custom_path" ]; then
25+ mkdir -p "$_custom_path"
26+ if [ $? -ne 0 ]; then
27+ cur_date="$(date +"%Y-%m-%d %H:%M:%S")"
28+ echo "[Common] [$cur_date] [ERROR]: create $_custom_path failed."
29+ return 1
30+ fi
31+ fi
32+ done < $_custom_path_file
33+ return 0
34+}
35+ 
36+py_version_check(){
37+ local pyver_set="3.7 3.8 3.9 3.10 3.11"
38+ local cur_date="$(date +"%Y-%m-%d %H:%M:%S")"
39+ which python3 > /dev/null 2>&1
40+ if [ $? -eq 0 ]; then
41+ local python_version="$(python3 --version 2>&1 | head -n 1)"
42+ local python3_version=$(echo "$python_version" | sed -n 's/.*[^\.0-9]\([0-9]\+\.[0-9]\+\).*/\1/p')
43+ if [ "x$python3_version" != "x" ]; then
44+ for ver in $pyver_set; do
45+ if [ "x$ver" = "x$python3_version" ]; then
46+ return 0
47+ fi
48+ done
49+
50+ echo "[Common] [$cur_date] [WARNING]: $python_version is not in Python3.7.x, Python3.8.x, Python3.9.x, Python3.10.x, Python3.11.x."
51+ return 1
52+ else
53+ echo "[Common] [$cur_date] [WARNING]: $python_version cannot be identified as a standard version, please check manually."
54+ return 1
55+ fi
56+ else
57+ echo "[Common] [$cur_date] [WARNING]: python3 is not found."
58+ return 1
59+ fi
60+}
61+ 
Ascripts/package/common/sh/common_interface.csh+37-0
@@ -0,0 +1,37 @@
1+#!/bin/csh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+set func_name = "$1"
13+switch ( "$func_name" )
14+ case "mk_custom_path":
15+ if ( "`id -u`" == 0 ) then
16+ exit 0
17+ endif
18+ set file_path = "$2"
19+ foreach line ("` cat $file_path `")
20+ set custom_path = "`echo '$line' | cut --only-delimited -d= -f2`"
21+ if ( "$custom_path" == "" ) then
22+ continue
23+ endif
24+ set custom_path = "` eval echo $custom_path `"
25+ if ( ! -d "$custom_path" ) then
26+ mkdir -p "$custom_path"
27+ if ( $status != 0 ) then
28+ set cur_date = "`date +'%Y-%m-%d %H:%M:%S'`"
29+ echo "[Common] [$cur_date] [ERROR]: create $custom_path failed."
30+ exit 1
31+ endif
32+ endif
33+ end
34+ breaksw
35+ default:
36+ breaksw
37+endsw
Ascripts/package/common/sh/common_interface.fish+33-0
@@ -0,0 +1,33 @@
1+#!/usr/bin/env fish
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+function mk_custom_path
13+ set -l custom_file_path $argv[1]
14+ if test (id -u) -eq 0
15+ return 0
16+ end
17+ while read line
18+ set -l _custom_path (echo "$line" | cut --only-delimited -d= -f2)
19+ if test -z $_custom_path
20+ continue
21+ end
22+ set -l _custom_path (eval echo "$_custom_path")
23+ if not test -d $_custom_path
24+ mkdir -p "$_custom_path"
25+ if not test $status -eq 0
26+ set -l cur_date (date +"%Y-%m-%d %H:%M:%S")
27+ echo "[Common] [$cur_date] [ERROR]: create $_custom_path failed."
28+ return 1
29+ end
30+ end
31+ end < $custom_file_path
32+ return 0
33+end
Ascripts/package/common/sh/common_interface.sh+61-0
@@ -0,0 +1,61 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+mk_custom_path() {
13+ if [ $(id -u) -eq 0 ]; then
14+ return 0
15+ fi
16+ local _custom_path_file="$1"
17+ while read line || [ -n "$line" ]
18+ do
19+ local _custom_path="$(echo "$line" | cut --only-delimited -d= -f2)"
20+ if [ -z "$_custom_path" ]; then
21+ continue
22+ fi
23+ eval "_custom_path=$_custom_path"
24+ if [ ! -d "$_custom_path" ]; then
25+ mkdir -p "$_custom_path"
26+ if [ $? -ne 0 ]; then
27+ cur_date="$(date +"%Y-%m-%d %H:%M:%S")"
28+ echo "[Common] [$cur_date] [ERROR]: create $_custom_path failed."
29+ return 1
30+ fi
31+ fi
32+ done < $_custom_path_file
33+ return 0
34+}
35+ 
36+py_version_check(){
37+ local pyver_set="3.7 3.8 3.9 3.10 3.11"
38+ local cur_date="$(date +"%Y-%m-%d %H:%M:%S")"
39+ which python3 > /dev/null 2>&1
40+ if [ $? -eq 0 ]; then
41+ local python_version="$(python3 --version 2>&1 | head -n 1)"
42+ local python3_version=$(echo "$python_version" | sed -n 's/.*[^\.0-9]\([0-9]\+\.[0-9]\+\).*/\1/p')
43+ if [ "x$python3_version" != "x" ]; then
44+ for ver in $pyver_set; do
45+ if [ "x$ver" = "x$python3_version" ]; then
46+ return 0
47+ fi
48+ done
49+
50+ echo "[Common] [$cur_date] [WARNING]: $python_version is not in Python3.7.x, Python3.8.x, Python3.9.x, Python3.10.x, Python3.11.x."
51+ return 1
52+ else
53+ echo "[Common] [$cur_date] [WARNING]: $python_version cannot be identified as a standard version, please check manually."
54+ return 1
55+ fi
56+ else
57+ echo "[Common] [$cur_date] [WARNING]: python3 is not found."
58+ return 1
59+ fi
60+}
61+ 
Ascripts/package/common/sh/install_common_parser.sh+2200-0
Ascripts/package/common/sh/multi_version.inc+305-0
@@ -0,0 +1,305 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+# 多版本函数库
13+# 创建版本目录
14+create_version_dir() {
15+ local install_path="$1"
16+ local version_dir="$2"
17+ local username="$3"
18+ local usergroup="$4"
19+ local install_for_all="$5"
20+ local ret
21+ 
22+ if [ ! -d "${install_path}/${version_dir}" ]; then
23+ make_dir "${install_path}/${version_dir}"
24+ ret="$?" && [ $ret -ne 0 ] && return $ret
25+ fi
26+ 
27+ # 约束版本号目录权限
28+ change_mod "${install_path}/${version_dir}" "750" "${install_for_all}"
29+ ret="$?" && [ $ret -ne 0 ] && return $ret
30+ 
31+ change_own "${install_path}/${version_dir}" "${username}:${usergroup}"
32+ ret="$?" && [ $ret -ne 0 ] && return $ret
33+ 
34+ return 0
35+}
36+ 
37+# 检查版本包安装结果
38+check_version_install() {
39+ local install_path="$1"
40+ local version_dir="$2"
41+ local package="$3"
42+ local version_info_path
43+ 
44+ get_package_version_info "version_info_path" "$install_path" "$version_dir" "$package"
45+ 
46+ if [ ! -f "$version_info_path" ]; then
47+ comm_log "ERROR" "$version_info_path doesn't exist in check version install!"
48+ return 1
49+ fi
50+ return 0
51+}
52+ 
53+# 安装latest管理器
54+install_latest_manager() {
55+ local var_path="$1"
56+ shift 1
57+ sh latest_manager/install.sh --install-path="$var_path" "$@"
58+}
59+ 
60+# 卸载latest管理器
61+uninstall_latest_manager_for_upgrade() {
62+ local var_path="$1"
63+ ${var_path}/manager/uninstall.sh --upgrade
64+}
65+ 
66+# 升级latest管理器
67+upgrade_latest_manager() {
68+ local var_path="$1"
69+ local latest_version_info latest_manager_version local_manager_version
70+ 
71+ latest_version_info="$var_path/manager/version.info"
72+ if [ -f "$latest_version_info" ]; then
73+ get_version latest_manager_version "$latest_version_info"
74+ get_version local_manager_version "latest_manager/version.info"
75+ 
76+ set_default "latest_manager_version" "$latest_manager_version" "0"
77+ set_default "local_manager_version" "$local_manager_version" "0"
78+ 
79+ if [ "$latest_manager_version" -lt "$local_manager_version" ]; then
80+ uninstall_latest_manager_for_upgrade "$var_path"
81+ install_latest_manager "$var_path" "--upgrade"
82+ fi
83+ else
84+ install_latest_manager "$var_path"
85+ fi
86+}
87+ 
88+get_install_for_all_param() {
89+ local _outvar="$1"
90+ local _install_for_all="$2"
91+ local _result=""
92+ 
93+ if [ "$_install_for_all" = "y" ]; then
94+ _result="--install-for-all"
95+ fi
96+ 
97+ eval "${_outvar}=\"${_result}\""
98+}
99+ 
100+get_docker_root_param() {
101+ local _outvar="$1"
102+ local _docker_root="$2"
103+ local _result=""
104+ 
105+ if [ "$_docker_root" != "" ]; then
106+ _result="--docker-root \\\"$_docker_root\\\""
107+ fi
108+ 
109+ eval "${_outvar}=\"${_result}\""
110+}
111+ 
112+# 通知latest管理器
113+notify_latest_manager() {
114+ local var_path="$1"
115+ local package="$2"
116+ local version="$3"
117+ local version_dir="$4"
118+ local install_for_all="$5"
119+ local docker_root="$6"
120+ local ext_params="$7"
121+ local operation="$8"
122+ local ret package_dir install_for_all_param docker_root_param
123+ 
124+ if [ ! -f "$var_path/manager.sh" ]; then
125+ return 1
126+ fi
127+ 
128+ if [ "${USE_SHARE_INFO}" == "y" ]; then
129+ ext_params="$ext_params --use-share-info"
130+ fi
131+ 
132+ get_package_dir package_dir "$package"
133+ get_install_for_all_param "install_for_all_param" "$install_for_all"
134+ get_docker_root_param "docker_root_param" "$docker_root"
135+ 
136+ eval "\"$var_path/manager.sh\"" --version "\"$version\"" --version-dir "\"$version_dir\"" \
137+ --package "\"$package\"" --package-dir "\"$package_dir\"" \
138+ "$install_for_all_param" "$docker_root_param" "$ext_params" "$operation"
139+ ret="$?" && [ $ret -ne 0 ] && return $ret
140+ 
141+ return 0
142+}
143+ 
144+# 通知latest管理器安装完成
145+notify_latest_manager_installed() {
146+ local local_manager_version
147+ local ext_params
148+ 
149+ get_version local_manager_version "latest_manager/version.info"
150+ set_default "local_manager_version" "$local_manager_version" "0"
151+ 
152+ if [ "$INCREMENT" = "y" ]; then
153+ ext_params="--serial $local_manager_version --increment"
154+ else
155+ ext_params="--serial $local_manager_version"
156+ fi
157+ 
158+ notify_latest_manager "$@" "$ext_params" "package_installed"
159+}
160+ 
161+# 通知latest管理器创建软链
162+# 老版本全部卸载后,latest回滚到新版本时,会用老版本的install_common_parser.sh,
163+# 调用新版本的--create-package-latest-softlink,会走到这个流程。
164+notify_latest_manager_create_softlink() {
165+ notify_latest_manager "$@" "" "package_create_softlink"
166+}
167+ 
168+# 通知latest管理器删除软链
169+notify_latest_manager_remove_softlink() {
170+ notify_latest_manager "$@" "" "package_remove_softlink"
171+}
172+ 
173+# 通知latest管理器准备卸载
174+notify_latest_manager_pre_uninstall() {
175+ notify_latest_manager "$@" "" "package_pre_uninstall"
176+}
177+ 
178+# 通知latest管理器卸载完成
179+notify_latest_manager_uninstalled() {
180+ local is_recreate_softlink="$1"
181+ local recreate_softlink=""
182+ local ret
183+ shift 1
184+ 
185+ if [ "$is_recreate_softlink" = "y" ]; then
186+ recreate_softlink="--recreate-softlink"
187+ fi
188+ 
189+ notify_latest_manager "$@" "$recreate_softlink" "package_uninstalled"
190+ ret="$?" && [ $ret -ne 0 ] && return $ret
191+ 
192+ return 0
193+}
194+ 
195+# 多版本安装流程
196+multi_version_install() {
197+ local install_type="$1"
198+ local install_path="$2"
199+ local filelist_path="$3"
200+ local package="$4"
201+ local feature_param="$5"
202+ local version="$6"
203+ local version_dir="$7"
204+ local username="$8"
205+ local usergroup="$9"
206+ local setenv="${10}"
207+ local is_upgrade="${11}"
208+ local docker_root="${12}"
209+ local custom_options="${13}"
210+ local install_for_all="${14}"
211+ local ret total_ret="0" pkg_running_version version_pair_arr last_version last_version_dir
212+ 
213+ create_version_dir "${install_path}" "${version_dir}" "${username}" "${usergroup}" "${install_for_all}"
214+ ret="$?" && [ $ret -ne 0 ] && return $ret
215+ 
216+ version_install "${install_type}" "${install_path}" "${filelist_path}" "${package}" "${feature_param}" \
217+ "${version_dir}" "${username}" "${usergroup}" "${setenv}" "${is_upgrade}" "${docker_root}" "${custom_options}"
218+ ret="$?" && [ $ret -ne 0 ] && return $ret
219+ 
220+ check_version_install "$install_path" "$version_dir" "$package"
221+ ret="$?" && [ $ret -ne 0 ] && return $ret
222+ 
223+ return ${total_ret}
224+}
225+ 
226+# 删除latest下空目录
227+del_empty_dirs_in_latest() {
228+ local install_type="$1"
229+ local install_path="$2"
230+ local latest_dir="$3"
231+ local filelist_path="$4"
232+ local feature_param="$5"
233+ local ret
234+ 
235+ create_stash_mod "${install_path}/${latest_dir}"
236+ ret="$?" && [ $ret -ne 0 ] && return $ret
237+ 
238+ foreach_filelist "filter_common_dirs" "reset_mod_dirs_with_stash_mod" "${install_type}" "${install_path}/${latest_dir}" "mkdir" \
239+ "${filelist_path}" "${feature_param}" "no" "normal"
240+ ret="$?" && [ $ret -ne 0 ] && return $ret
241+ 
242+ foreach_filelist "filter_common_dirs" "remove_install_dirs" "${install_type}" "${install_path}/${latest_dir}" "mkdir" \
243+ "${filelist_path}" "${feature_param}" "reverse" "normal"
244+ ret="$?" && [ $ret -ne 0 ] && return $ret
245+ 
246+ foreach_stashmod "restore_stash_mod" "${install_path}/${latest_dir}" "reverse"
247+ ret="$?" && [ $ret -ne 0 ] && return $ret
248+ 
249+ remove_stash_mod "${install_path}/${latest_dir}"
250+ ret="$?" && [ $ret -ne 0 ] && return $ret
251+ 
252+ return 0
253+}
254+ 
255+# 多版本卸载流程
256+multi_version_uninstall() {
257+ local install_type="$1"
258+ local install_path="$2"
259+ local filelist_path="$3"
260+ local package="$4"
261+ local feature_param="$5"
262+ local version="$6"
263+ local version_dir="$7"
264+ local username="$8"
265+ local usergroup="$9"
266+ local docker_root="${10}"
267+ local custom_options="${11}"
268+ local is_recreate_softlink="${12}"
269+ local tmp_root tmp_filelist_path
270+ local ret total_ret=0 install_path_full="" is_running="" is_upgrade
271+ local running_packages is_final_running="false"
272+ 
273+ check_param_not_empty "usergroup" "need set usergroup parameter in multi version uninstall!"
274+ ret="$?" && [ ${ret} -ne 0 ] && return ${ret}
275+ 
276+ get_tmp_root "tmp_root"
277+ tmp_filelist_path=$(mktemp "$tmp_root/filelist_XXXXXX" || exit 1)
278+ cp -f "${filelist_path}" "${tmp_filelist_path}"
279+ 
280+ if [ $? -ne 0 ]; then
281+ log "ERROR" "cp -f ${filelist_path} ${tmp_filelist_path} failed!"
282+ exit 1
283+ fi
284+ 
285+ del_tmp_filelist="rm -f \"${tmp_filelist_path}\""
286+ 
287+ version_uninstall "${install_type}" "${install_path}" "${filelist_path}" "${package}" "${feature_param}" \
288+ "${version_dir}" "${username}" "${docker_root}" "${custom_options}"
289+ if [ $? -ne 0 ]; then
290+ eval "${del_tmp_filelist}"
291+ return 1
292+ fi
293+ 
294+ # 删除临时文件tmp_filelist_path
295+ eval "${del_tmp_filelist}"
296+ 
297+ # 删除版本空目录
298+ is_dir_empty "${install_path}/${version_dir}"
299+ if [ $? -eq 0 ]; then
300+ remove_dir_icp "${install_path}/${version_dir}"
301+ ret="$?" && [ $ret -ne 0 ] && total_ret="1"
302+ fi
303+ 
304+ return ${total_ret}
305+}
Ascripts/package/common/sh/script_operator.inc+515-0
@@ -0,0 +1,515 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+# 公共脚本操作库
13+ENV_SHELL_TYPES="bash csh fish"
14+ 
15+# 检查参数。
16+__check_param() {
17+ local name="$1"
18+ local value
19+ 
20+ value="$(eval echo \${${name}})"
21+ 
22+ if [ "${value}" = "" ]; then
23+ log "ERROR" "need set ${name} parameter!"
24+ exit 1
25+ fi
26+}
27+ 
28+# 设置用户家目录路径
29+__set_userpath() {
30+ local username="$1"
31+ local docker_root="$2"
32+ 
33+ if [ -z "${docker_root}" ] || [ "${docker_root}" = "/" ]; then
34+ userpath="$(eval echo "~${username}")"
35+ else
36+ if [ "${username}" = "root" ]; then
37+ userpath="${docker_root}/root"
38+ else
39+ userpath="${docker_root}/home/${username}"
40+ fi
41+ fi
42+}
43+ 
44+# 获取setenv.[shell]路径正则
45+get_setenv_path_regex() {
46+ local _outvar="$1"
47+ local _package="$2"
48+ local _shell_type="$3"
49+ # setenv传入路径正则规则
50+ # 必须以/开头(绝对路径)
51+ # 必须以/${package}/bin/setenv.${shell_type}结尾
52+ local _path_regex="\/\(.\+\/\)\?${_package}\/bin\/setenv.${_shell_type}"
53+ 
54+ eval "${_outvar}=\"${_path_regex}\""
55+}
56+ 
57+# 创建setenv文件
58+__create_setenv_file() {
59+ local file="$1"
60+ local shell_type="$2"
61+ local add_multi_version_param="$3"
62+ local install_path="$4"
63+ local ret
64+ 
65+ if [ ! -f "${file}" ]; then
66+ echo "#!/usr/bin/env ${shell_type}" > ${file}
67+ ret="$?" && [ $ret -ne 0 ] && return $ret
68+ 
69+ if [ "${add_multi_version_param}" = "true" ]; then
70+ if [ "${shell_type}" = "bash" ]; then
71+ echo "export ASCEND_HOME_PATH=\"${install_path}\"" >> "${file}"
72+ ret="$?" && [ $ret -ne 0 ] && return $ret
73+ elif [ "${shell_type}" = "fish" ]; then
74+ echo "set -gx ASCEND_HOME_PATH \"${install_path}\"" >> "${file}"
75+ ret="$?" && [ $ret -ne 0 ] && return $ret
76+ elif [ "${shell_type}" = "csh" ]; then
77+ echo "setenv ASCEND_HOME_PATH \"${install_path}\"" >> "${file}"
78+ ret="$?" && [ $ret -ne 0 ] && return $ret
79+ fi
80+ fi
81+ fi
82+ return 0
83+}
84+ 
85+# 是否存在ASCEND_HOME_PATH环境变量
86+has_ascend_home_path_env() {
87+ local file="$1"
88+ [ ! -f "${file}" ] && return 0
89+ grep "\<ASCEND_HOME_PATH\>" "${file}" > /dev/null 2>&1
90+}
91+ 
92+# 添加ASCEND_HOME_PATH环境变量
93+add_ascend_home_path_env() {
94+ local file="$1"
95+ local shell_type="$2"
96+ local install_path="$3"
97+ 
98+ if [ "${shell_type}" = "bash" ]; then
99+ sed -i "1aexport ASCEND_HOME_PATH=\"${install_path}\"" "${file}"
100+ elif [ "${shell_type}" = "fish" ]; then
101+ sed -i "1aset -gx ASCEND_HOME_PATH \"${install_path}\"" "${file}"
102+ elif [ "${shell_type}" = "csh" ]; then
103+ sed -i "1asetenv ASCEND_HOME_PATH \"${install_path}\"" "${file}"
104+ fi
105+}
106+ 
107+# 删除rcfile中的source <path>
108+__remove_path_regex() {
109+ local path_regex="$1"
110+ local rcfile="$2"
111+ 
112+ if [ -f "${rcfile}" ]; then
113+ sed -i "/source ${path_regex}\( \"multi_version\"\)\?$/d" "${rcfile}"
114+ if [ $? -ne 0 ]; then
115+ log "ERROR" "remove ${rcfile} source command failed!"
116+ exit 1
117+ fi
118+ fi
119+}
120+ 
121+# 获取setenv.${shell_type}文件路径
122+get_setenv_filepath() {
123+ local _outvar="$1"
124+ local _install_path="$2"
125+ local _shell_type="$3"
126+ 
127+ eval "${_outvar}=\"${_install_path}/bin/setenv.${_shell_type}\""
128+}
129+ 
130+# setenv.[shell]总脚本中添加一条语句
131+add_setenv_cmd() {
132+ local install_path="$1"
133+ local setenv_filepath="$2"
134+ local package="$3"
135+ local shell_type="$4"
136+ local username="$5"
137+ local usergroup="$6"
138+ local add_multi_version_param="$7"
139+ local path_regex config_path multi_version_param ret
140+ 
141+ get_setenv_path_regex "path_regex" "${package}" "${shell_type}"
142+ 
143+ get_setenv_filepath "config_path" "$install_path" "$shell_type"
144+ __create_setenv_file "${config_path}" "${shell_type}" "${add_multi_version_param}" "${install_path}"
145+ ret="$?" && [ $ret -ne 0 ] && return $ret
146+ 
147+ change_own "${config_path}" "${username}:${usergroup}"
148+ ret="$?" && [ $ret -ne 0 ] && return $ret
149+ 
150+ change_mod "${config_path}" "${SETENV_WRITEABLE_MOD}" ""
151+ ret="$?" && [ $ret -ne 0 ] && return $ret
152+ 
153+ __remove_path_regex "${path_regex}" "${config_path}"
154+ ret="$?" && [ $ret -ne 0 ] && return $ret
155+ 
156+ if [ "${add_multi_version_param}" = "true" ]; then
157+ multi_version_param=" \"multi_version\""
158+ fi
159+ 
160+ if [ "${shell_type}" = "bash" ] || [ "${shell_type}" = "fish" ]; then
161+ echo "source ${setenv_filepath}${multi_version_param}" >> "${config_path}"
162+ ret="$?" && [ $ret -ne 0 ] && return $ret
163+ elif [ "${shell_type}" = "csh" ]; then
164+ echo "set argv=(\"${setenv_filepath}\"${multi_version_param}); source ${setenv_filepath}" >> "${config_path}"
165+ ret="$?" && [ $ret -ne 0 ] && return $ret
166+ fi
167+ change_mod "${config_path}" "${SETENV_MOD}" "${INSTALL_FOR_ALL}"
168+ ret="$?" && [ $ret -ne 0 ] && return $ret
169+ 
170+ return 0
171+}
172+ 
173+# 修改路径的own
174+__chown_path() {
175+ local path="$1"
176+ local username="$2"
177+ local usergroup="$3"
178+ 
179+ if [ "${username}" != "" ] && [ "${usergroup}" != "" ]; then
180+ chown -h "${username}:${usergroup}" "${path}"
181+ if [ $? -ne 0 ]; then
182+ log "ERROR" "${path} chown failed!"
183+ exit 1
184+ fi
185+ fi
186+ 
187+ return 0
188+}
189+ 
190+# 创建配置所在目录
191+__create_config_dir() {
192+ local dir="$1"
193+ local username="$2"
194+ local usergroup="$3"
195+ local ret
196+ 
197+ if [ ! -d "${dir}" ]; then
198+ mkdir -p "${dir}"
199+ ret="$?" && [ $ret -ne 0 ] && return $ret
200+ 
201+ __chown_path "${dir}" "${username}" "${usergroup}"
202+ ret="$?" && [ $ret -ne 0 ] && return $ret
203+ fi
204+ 
205+ return 0
206+}
207+ 
208+# 创建配置文件
209+__create_config_file() {
210+ local file="$1"
211+ local username="$2"
212+ local usergroup="$3"
213+ local ret
214+ 
215+ if [ ! -f "${file}" ]; then
216+ touch "${file}"
217+ ret="$?" && [ $ret -ne 0 ] && return $ret
218+ 
219+ __chown_path "${file}" "${username}" "${usergroup}"
220+ ret="$?" && [ $ret -ne 0 ] && return $ret
221+ fi
222+ 
223+ return 0
224+}
225+ 
226+# 添加source setenv.[shell]脚本到rc文件中
227+add_env_rc() {
228+ local install_path="$1"
229+ local setenv_filepath="$2"
230+ local package="$3"
231+ local shell_type="$4"
232+ local setenv="$5"
233+ local username="$6"
234+ local usergroup="$7"
235+ local add_multi_version_param="$8"
236+ local docker_root="$9"
237+ local path_suffix="${package}/bin/setenv.${shell_type}"
238+ local path_regex
239+ local matched
240+ local userpath
241+ local config_path
242+ local ret
243+ 
244+ __check_param "package"
245+ __check_param "username"
246+ __check_param "usergroup"
247+ 
248+ echo "${shell_type}" | grep -E "^(bash|fish|csh)$" > /dev/null
249+ if [ $? -ne 0 ]; then
250+ log "ERROR" "shell type ${shell_type} not support!"
251+ exit 1
252+ fi
253+ 
254+ get_setenv_path_regex "path_regex" "${package}" "${shell_type}"
255+ 
256+ matched="$(echo "${setenv_filepath}" | sed -n "/^${path_regex}$/p")"
257+ if [ -z "${matched}" ]; then
258+ log "ERROR" "setenv filepath is illegal, should endswith ${path_suffix}"
259+ exit 1
260+ fi
261+ 
262+ if [ "${setenv}" = "y" ]; then
263+ __set_userpath "${username}" "${docker_root}"
264+ 
265+ if [ "${shell_type}" = "bash" ]; then
266+ config_path="${userpath}/.bashrc"
267+ __create_config_dir "${userpath}" "${username}" "${usergroup}"
268+ ret="$?" && [ $ret -ne 0 ] && return $ret
269+ 
270+ __create_config_file "${config_path}" "${username}" "${usergroup}"
271+ ret="$?" && [ $ret -ne 0 ] && return $ret
272+ 
273+ __remove_path_regex "${path_regex}" "${config_path}"
274+ ret="$?" && [ $ret -ne 0 ] && return $ret
275+ 
276+ echo "source ${setenv_filepath}" >> "${config_path}"
277+ ret="$?" && [ $ret -ne 0 ] && return $ret
278+ elif [ "${shell_type}" = "fish" ]; then
279+ config_path="${userpath}/.config/fish/config.fish"
280+ __create_config_dir "${userpath}" "${username}" "${usergroup}"
281+ ret="$?" && [ $ret -ne 0 ] && return $ret
282+ 
283+ __create_config_dir "${userpath}/.config" "${username}" "${usergroup}"
284+ ret="$?" && [ $ret -ne 0 ] && return $ret
285+ 
286+ __create_config_dir "${userpath}/.config/fish" "${username}" "${usergroup}"
287+ ret="$?" && [ $ret -ne 0 ] && return $ret
288+ 
289+ __create_config_file "${config_path}" "${username}" "${usergroup}"
290+ ret="$?" && [ $ret -ne 0 ] && return $ret
291+ 
292+ __remove_path_regex "${path_regex}" "${config_path}"
293+ ret="$?" && [ $ret -ne 0 ] && return $ret
294+ 
295+ echo "source ${setenv_filepath}" >> "${config_path}"
296+ ret="$?" && [ $ret -ne 0 ] && return $ret
297+ elif [ "${shell_type}" = "csh" ]; then
298+ config_path="${userpath}/.cshrc"
299+ __create_config_dir "${userpath}" "${username}" "${usergroup}"
300+ ret="$?" && [ $ret -ne 0 ] && return $ret
301+ 
302+ __create_config_file "${config_path}" "${username}" "${usergroup}"
303+ ret="$?" && [ $ret -ne 0 ] && return $ret
304+ 
305+ __remove_path_regex "${path_regex}" "${config_path}"
306+ ret="$?" && [ $ret -ne 0 ] && return $ret
307+ 
308+ echo "set argv=(\"${setenv_filepath}\"); source ${setenv_filepath}" >> "${config_path}"
309+ ret="$?" && [ $ret -ne 0 ] && return $ret
310+ fi
311+ fi
312+ 
313+ if [ ! -d "${install_path}/bin" ]; then
314+ # 如果bin目录不存在,则不处理
315+ return 0
316+ fi
317+ 
318+ add_setenv_cmd "${install_path}" "${setenv_filepath}" "${package}" "${shell_type}" "${username}" "${usergroup}" "${add_multi_version_param}"
319+}
320+ 
321+# 删除setenv文件,如果已经没有setenv内容
322+__remove_setenv_file_if_no_content() {
323+ local file="$1"
324+ local shell_type="$2"
325+ local num
326+ 
327+ if [ ! -f "${file}" ]; then
328+ return 0
329+ fi
330+ 
331+ num=$(grep "setenv.${shell_type}" ${file} | wc -l)
332+ if [ ${num} -eq 0 ]; then
333+ rm -f "${file}" > /dev/null 2>&1
334+ if [ $? -ne 0 ]; then
335+ log "WARNING" "Delete file:${file} failed, please delete it by yourself."
336+ fi
337+ fi
338+}
339+ 
340+# 从rc文件中删除source setenv.[shell]
341+del_env_rc() {
342+ local install_path="$1"
343+ local setenv_filepath="$2"
344+ local shell_type="$3"
345+ local username="$4"
346+ local docker_root="$5"
347+ local path_regex
348+ local userpath
349+ local config_path
350+ local oldmod
351+ 
352+ __check_param "username"
353+ 
354+ echo "${shell_type}" | grep -E "^(bash|fish|csh)$" > /dev/null
355+ if [ $? -ne 0 ]; then
356+ log "ERROR" "shell type ${shell_type} not support!"
357+ exit 1
358+ fi
359+ 
360+ __set_userpath "${username}" "${docker_root}"
361+ 
362+ # 将路径中的/转换为\/
363+ path_to_regex "path_regex" "${setenv_filepath}"
364+ 
365+ if [ "${shell_type}" = "bash" ]; then
366+ __remove_path_regex "${path_regex}" "${userpath}/.bashrc"
367+ ret="$?" && [ $ret -ne 0 ] && return $ret
368+ elif [ "${shell_type}" = "fish" ]; then
369+ __remove_path_regex "${path_regex}" "${userpath}/.config/fish/config.fish"
370+ ret="$?" && [ $ret -ne 0 ] && return $ret
371+ elif [ "${shell_type}" = "csh" ]; then
372+ __remove_path_regex "${path_regex}" "${userpath}/.cshrc"
373+ ret="$?" && [ $ret -ne 0 ] && return $ret
374+ fi
375+ 
376+ get_setenv_filepath "config_path" "$install_path" "$shell_type"
377+ 
378+ if [ ! -f "${config_path}" ]; then
379+ # 如果文件不存在,则不处理
380+ return 0
381+ fi
382+ 
383+ get_file_mod "oldmod" "${config_path}"
384+ change_mod "${config_path}" "${SETENV_WRITEABLE_MOD}" ""
385+ ret="$?" && [ $ret -ne 0 ] && return $ret
386+ 
387+ __remove_path_regex "${path_regex}" "${config_path}"
388+ ret="$?" && [ $ret -ne 0 ] && return $ret
389+ 
390+ change_mod "${config_path}" "${oldmod}" ""
391+ ret="$?" && [ $ret -ne 0 ] && return $ret
392+ 
393+ __remove_setenv_file_if_no_content "${config_path}" "${shell_type}"
394+}
395+ 
396+# 移除路径中的docker_root
397+remove_path_docker_root() {
398+ local _outvar="$1"
399+ local _path="$2"
400+ local _docker_root="$3"
401+ local _path_rpdr="$(echo "${_path}" | sed "s/^.\{${#_docker_root}\}//")"
402+ 
403+ eval "${_outvar}=\"${_path_rpdr}\""
404+}
405+ 
406+# 获取脚本路径与脚本真实路径(docker_root)
407+get_shell_path_and_shell_path_real() {
408+ local _outvar="$1"
409+ local _install_path="$2"
410+ local _package="$3"
411+ local _shell_filename="$4"
412+ local _docker_root="$5"
413+ local _package_dirpath _shell_path _shell_path_real
414+ 
415+ get_package_dirpath "_package_dirpath" "${_package}"
416+ _shell_path_real="${_install_path}/${_package_dirpath}/bin/${_shell_filename}"
417+ 
418+ if [ "${_docker_root}" != "" ]; then
419+ # 移除脚本路径中的docker_root前缀
420+ remove_path_docker_root "_shell_path" "${_shell_path_real}" "${_docker_root}"
421+ else
422+ _shell_path="${_shell_path_real}"
423+ fi
424+ 
425+ eval "${_outvar}=\"${_shell_path} ${_shell_path_real}\""
426+}
427+ 
428+# 增加prereq_check
429+add_prereq_check() {
430+ local install_path="$1"
431+ local package="$2"
432+ local username="$3"
433+ local usergroup="$4"
434+ local docker_root="$5"
435+ local ret
436+ 
437+ for type in ${ENV_SHELL_TYPES}; do
438+ local shell_path_pair shell_path shell_path_real
439+ 
440+ get_shell_path_and_shell_path_real "shell_path_pair" "${install_path}" "${package}" "prereq_check.${type}" "${docker_root}"
441+ __index_list "${shell_path_pair}" 0 "shell_path" 1 "shell_path_real"
442+ [ ! -f "${shell_path_real}" ] && continue
443+ 
444+ local common_path=${install_path}/bin/prereq_check.${type}
445+ local path_regex="\/\(.\+\/\)\?${package}\/bin\/prereq_check.${type}"
446+ if [ -f "${common_path}" ]; then
447+ chmod u+w ${common_path}
448+ 
449+ sed -i "/^${path_regex}$/d" ${common_path}
450+ ret="$?" && [ $ret -ne 0 ] && return $ret
451+ 
452+ echo "${shell_path}" >> ${common_path}
453+ ret="$?" && [ $ret -ne 0 ] && return $ret
454+ 
455+ chmod u-w ${common_path}
456+ else
457+ echo "#!/usr/bin/env ${type}" > ${common_path}
458+ ret="$?" && [ $ret -ne 0 ] && return $ret
459+ 
460+ echo "${shell_path}" >> ${common_path}
461+ ret="$?" && [ $ret -ne 0 ] && return $ret
462+ 
463+ change_mod "${common_path}" "${SETENV_MOD}" "${INSTALL_FOR_ALL}"
464+ ret="$?" && [ $ret -ne 0 ] && return $ret
465+ fi
466+ change_own "${common_path}" "${username}:${usergroup}"
467+ ret="$?" && [ $ret -ne 0 ] && return $ret
468+ done
469+ 
470+ return 0
471+}
472+ 
473+# 删除prereq_check
474+del_prereq_check() {
475+ local install_path="$1"
476+ local package="$2"
477+ local docker_root="$3"
478+ local ret
479+ 
480+ for type in ${ENV_SHELL_TYPES}; do
481+ local shell_path_pair shell_path shell_path_real
482+ 
483+ get_shell_path_and_shell_path_real "shell_path_pair" "${install_path}" "${package}" "prereq_check.${type}" "${docker_root}"
484+ __index_list "${shell_path_pair}" 0 "shell_path" 1 "shell_path_real"
485+ 
486+ local common_path="${install_path}/bin/prereq_check.${type}"
487+ 
488+ [ ! -f "${common_path}" ] && continue
489+ 
490+ chmod u+w "${common_path}"
491+ local path_regex="\/\(.\+\/\)\?${package}\/bin\/prereq_check.${type}"
492+ 
493+ sed -i "/^${path_regex}$/d" "${common_path}"
494+ ret="$?" && [ $ret -ne 0 ] && return $ret
495+ 
496+ chmod u-w "${common_path}"
497+ 
498+ local num=$(grep -r "prereq_check.${type}" "${common_path}" | wc -l)
499+ if [ "${num}" -eq 0 ]; then
500+ rm -f "${common_path}"
501+ fi
502+ done
503+ 
504+ return 0
505+}
506+ 
507+# 设置环境变量
508+add_setenv() {
509+ return 0
510+}
511+ 
512+# 删除环境变量
513+del_setenv() {
514+ return 0
515+}
Ascripts/package/common/sh/version_cfg.inc+1027-0
@@ -0,0 +1,1027 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+# 版本状态配置文件处理
13+# 版本状态配置文件格式为
14+# # version: 1.0
15+# runtime_running_version=[1.80:CANN-1.80]
16+# compiler_running_version=[1.80:CANN-1.80]
17+# toolkit_running_version=[1.80:CANN-1.80]
18+# fwkplugin_running_version=[1.80:CANN-1.80]
19+# runtime_installed_version=[1.79:CANN-1.79][1.80:CANN-1.80]
20+# compiler_installed_version=[1.79:CANN-1.79][1.80:CANN-1.80][1.81:CANN-1.81]
21+# toolkit_installed_version=[1.79:CANN-1.79][1.80:CANN-1.80][1.81:CANN-1.81]
22+# fwkplugin_installed_version=[1.79:CANN-1.79][1.80:CANN-1.80][1.81:CANN-1.81]
23+ 
24+# 子包会source本文件,本文件中调用的函数,都需要在文件内找到定义
25+ 
26+# latest目录名
27+LATEST_DIR="latest"
28+ 
29+# 配置版本号
30+_CFG_VERSION="1.0"
31+ 
32+# 获取列表索引值
33+__index_list_by_cut() {
34+ local _list_ilbc="$1"
35+ local _cnt_ilbc _value_ilbc
36+ shift
37+ 
38+ while true; do
39+ if [ $# -eq 0 ]; then
40+ return 0
41+ fi
42+ _cnt_ilbc="${1}" && _cnt_ilbc=$((_cnt_ilbc+1))
43+ _value_ilbc="$(echo "${_list_ilbc}" | cut -d' ' -f"${_cnt_ilbc}")"
44+ eval "${2}=\"${_value_ilbc}\""
45+ shift 2
46+ done
47+ 
48+ return 0
49+}
50+ 
51+# 获取版本状态配置路径
52+get_version_cfg_path() {
53+ local _outvar="$1"
54+ local _dirpath="$2"
55+ eval "${_outvar}=\"${_dirpath}/version.cfg\""
56+}
57+ 
58+# 获取版本状态配置新路径
59+get_version_cfg_new_path() {
60+ local _outvar="$1"
61+ local _dirpath="$2"
62+ eval "${_outvar}=\"${_dirpath}/version.cfg.new\""
63+}
64+ 
65+# 解析配置行
66+parse_cfg_line() {
67+ local _outvar="$1"
68+ local _cfg_line="$2"
69+ local _cfg_items_pcl
70+ 
71+ _cfg_items_pcl=$(echo "${_cfg_line}" | awk '
72+ function join(items, len, sep, result, i) {
73+ if (len >= 1) {
74+ result = items[1]
75+ for (i = 2; i <= len; i++) {
76+ result = sprintf("%s%s%s", result, sep, items[i])
77+ }
78+ return result
79+ }
80+ return ""
81+ }
82+ 
83+ /^[A-Za-z0-9_-]+_version=/ {
84+ split($0, line_tokens, "=")
85+ len_attr_tokens = split(line_tokens[1], attr_tokens, "_")
86+ len_package_names = 0
87+ for (i = 1; i < len_attr_tokens - 1; i++) {
88+ len_package_names++
89+ package_names[len_package_names] = attr_tokens[i]
90+ }
91+ package_name = join(package_names, len_package_names, "_")
92+ attr_type = attr_tokens[len_attr_tokens-1]
93+ value = line_tokens[2]
94+ 
95+ printf("%s %s %s", package_name, attr_type, value)
96+ }
97+ ')
98+ eval "${_outvar}=\"${_cfg_items_pcl}\""
99+}
100+ 
101+# 解析配置版本号列表
102+unpack_versions() {
103+ local _outvar="$1"
104+ local _value="$2"
105+ 
106+ local _versions="$(echo "${_value}" | sed 's/\[//g')"
107+ _versions="$(echo "${_versions}" | sed 's/\]/ /g')"
108+ _versions="$(echo ${_versions})" # strip
109+ eval "${_outvar}=\"${_versions}\""
110+}
111+ 
112+# 打包配置版本号列表
113+pack_versions() {
114+ local _outvar="$1"
115+ local _versions="$2"
116+ 
117+ local _value="$(echo "${_versions}" | sed 's/ /\]\[/g')"
118+ eval "${_outvar}=\"[${_value}]\""
119+}
120+ 
121+# 解析版本序对
122+unpack_version_pair() {
123+ local _outvar="$1"
124+ local _value="$2"
125+ 
126+ eval "${_outvar}=\"$(echo "${_value}" | tr ':' ' ')\""
127+}
128+ 
129+# 打包运行版本配置
130+pack_running_version() {
131+ local _outvar="$1"
132+ local _package="$2"
133+ local _version="$3"
134+ local _version_dir="$4"
135+ 
136+ local _version_str
137+ pack_versions "_version_str" "${_version}:${_version_dir}"
138+ eval "${_outvar}=\"${_package}_running_version=${_version_str}\""
139+}
140+ 
141+# 打包升级版本配置
142+pack_upgrade_version() {
143+ local _outvar="$1"
144+ local _package="$2"
145+ local _version="$3"
146+ local _version_dir="$4"
147+ 
148+ local _version_str
149+ pack_versions "_version_str" "${_version}:${_version_dir}"
150+ eval "${_outvar}=\"${_package}_upgrade_version=${_version_str}\""
151+}
152+ 
153+# 检查配置版本
154+check_cfg_version() {
155+ local filepath="$1"
156+ local version
157+ 
158+ if [ ! -f "${filepath}" ]; then
159+ return 0
160+ fi
161+ 
162+ version=$(sed -n '1p' "${filepath}" | cut -d" " -f3)
163+ if [ "${version}" != "${_CFG_VERSION}" ]; then
164+ return 1
165+ fi
166+ return 0
167+}
168+ 
169+# 输出配置版本错误
170+error_check_cfg_version() {
171+ local filepath="$1"
172+ log "ERROR" "check ${filepath} version failed!"
173+}
174+ 
175+# 检查配置版本并输出报错日志
176+check_cfg_version_with_log() {
177+ local filepath="$1"
178+ local ret
179+ 
180+ check_cfg_version "${filepath}"
181+ ret="$?"
182+ if [ ${ret} -ne 0 ]; then
183+ error_check_cfg_version "${filepath}"
184+ return ${ret}
185+ fi
186+ return 0
187+}
188+ 
189+# 添加配置版本注释
190+add_cfg_version_comment() {
191+ local filepath="$1"
192+ sed -i "1i # version: ${_CFG_VERSION}" "${filepath}"
193+}
194+ 
195+# 移除子包版本
196+unset_package_version() {
197+ local dirpath="$1"
198+ local package="$2"
199+ local target_type="$3"
200+ local filepath filepath_new line cfg_items package_name attr_type
201+ local cleanup="" ret mod
202+ 
203+ get_version_cfg_path "filepath" "${dirpath}"
204+ get_version_cfg_new_path "filepath_new" "${dirpath}"
205+ 
206+ # 版本状态配置文件不存在则正常退出
207+ if [ ! -f "${filepath}" ]; then
208+ return 0
209+ fi
210+ 
211+ check_cfg_version_with_log "${filepath}"
212+ if [ $? -ne 0 ]; then
213+ return 1
214+ fi
215+ 
216+ # 保存当前目录的权限
217+ get_file_mod "mod" "${dirpath}"
218+ 
219+ # 添加目录写权限
220+ chmod u+w "${dirpath}"
221+ ret="$?" && [ $ret -ne 0 ] && return $ret
222+ 
223+ # 恢复权限
224+ cleanup="chmod ${mod} \"${dirpath}\""
225+ 
226+ rm -f "${filepath_new}"
227+ if [ $? -ne 0 ]; then
228+ log "ERROR" "delete ${filepath_new} failed!"
229+ eval "${cleanup}"
230+ return 1
231+ fi
232+ 
233+ while read line; do
234+ parse_cfg_line "cfg_items" "${line}"
235+ [ "${cfg_items}" = "" ] && continue
236+ 
237+ __index_list_by_cut "${cfg_items}" 0 "package_name" 1 "attr_type"
238+ 
239+ if [ "${attr_type}" = "${target_type}" ]; then
240+ if [ "${package_name}" != "${package}" ]; then
241+ echo "${line}" >> "${filepath_new}"
242+ fi
243+ else
244+ echo "${line}" >> "${filepath_new}"
245+ fi
246+ done < "${filepath}"
247+ 
248+ if [ -f "${filepath_new}" ]; then
249+ add_cfg_version_comment "${filepath_new}"
250+ mv -f "${filepath_new}" "${filepath}"
251+ if [ $? -ne 0 ]; then
252+ log "ERROR" "replace ${filepath} failed!"
253+ eval "${cleanup}"
254+ return 1
255+ fi
256+ else
257+ rm -f "${filepath}"
258+ if [ $? -ne 0 ]; then
259+ log "ERROR" "delete ${filepath} failed!"
260+ eval "${cleanup}"
261+ return 1
262+ fi
263+ fi
264+ 
265+ eval "${cleanup}"
266+ ret="$?" && [ $ret -ne 0 ] && return $ret
267+ 
268+ return 0
269+}
270+ 
271+# 获取子包版本
272+get_package_version_version_cfg() {
273+ local _outvar="$1"
274+ local _dirpath="$2"
275+ local _package="$3"
276+ local _target_type="$4"
277+ local _filepath _version_gpv
278+ 
279+ get_version_cfg_path "_filepath" "${_dirpath}"
280+ 
281+ if [ ! -f "${_filepath}" ]; then
282+ eval "${_outvar}=\"\""
283+ return 0
284+ fi
285+ 
286+ check_cfg_version "${_filepath}"
287+ if [ $? -ne 0 ]; then
288+ eval "${_outvar}=\"\""
289+ return 1
290+ fi
291+ 
292+ _version_gpv="$(grep "^${_package}_${_target_type}_version=" "${_filepath}" | cut -d= -f2-)"
293+ if [ "${_version_gpv}" != "" ]; then
294+ unpack_versions "_version_gpv" "${_version_gpv}"
295+ fi
296+ 
297+ eval "${_outvar}=\"${_version_gpv}\""
298+ return 0
299+}
300+ 
301+# 设置运行子包版本
302+set_running_package_version() {
303+ local dirpath="$1"
304+ local package="$2"
305+ local version="$3"
306+ local version_dir="$4"
307+ local filepath filepath_new line cfg_items package_name attr_type running_version
308+ local setted="false" cleanup="" ret mod
309+ 
310+ get_version_cfg_path "filepath" "${dirpath}"
311+ get_version_cfg_new_path "filepath_new" "${dirpath}"
312+ 
313+ check_cfg_version_with_log "${filepath}"
314+ if [ $? -ne 0 ]; then
315+ return 1
316+ fi
317+ 
318+ pack_running_version "running_version" "${package}" "${version}" "${version_dir}"
319+ 
320+ # 保存当前目录的权限
321+ get_file_mod "mod" "${dirpath}"
322+ 
323+ # 添加目录写权限
324+ chmod u+w "${dirpath}"
325+ ret="$?" && [ $ret -ne 0 ] && return $ret
326+ 
327+ # 恢复权限
328+ cleanup="chmod ${mod} \"${dirpath}\""
329+ 
330+ rm -f "${filepath_new}"
331+ if [ $? -ne 0 ]; then
332+ log "ERROR" "delete ${filepath_new} failed!"
333+ eval "${cleanup}"
334+ return 1
335+ fi
336+ 
337+ touch "${filepath}"
338+ while read line; do
339+ parse_cfg_line "cfg_items" "${line}"
340+ [ "${cfg_items}" = "" ] && continue
341+ 
342+ __index_list_by_cut "${cfg_items}" 0 "package_name" 1 "attr_type"
343+ 
344+ if [ "${attr_type}" = "running" ]; then
345+ if [ "${package_name}" = "${package}" ]; then
346+ if [ "${setted}" = "false" ]; then
347+ echo "${running_version}" >> "${filepath_new}"
348+ setted="true"
349+ fi
350+ else
351+ echo "${line}" >> "${filepath_new}"
352+ fi
353+ elif [ "${attr_type}" = "upgrade" ]; then
354+ if [ "${setted}" = "false" ]; then
355+ echo "${running_version}" >> "${filepath_new}"
356+ setted="true"
357+ fi
358+ echo "${line}" >> "${filepath_new}"
359+ elif [ "${attr_type}" = "installed" ]; then
360+ if [ "${setted}" = "false" ]; then
361+ echo "${running_version}" >> "${filepath_new}"
362+ setted="true"
363+ fi
364+ echo "${line}" >> "${filepath_new}"
365+ else
366+ echo "${line}" >> "${filepath_new}"
367+ fi
368+ done < "${filepath}"
369+ 
370+ if [ "${setted}" = "false" ]; then
371+ echo "${running_version}" >> "${filepath_new}"
372+ setted="true"
373+ fi
374+ 
375+ add_cfg_version_comment "${filepath_new}"
376+ mv -f "${filepath_new}" "${filepath}"
377+ if [ $? -ne 0 ]; then
378+ log "ERROR" "replace ${filepath} failed!"
379+ eval "${cleanup}"
380+ return 1
381+ fi
382+ 
383+ eval "${cleanup}"
384+ ret="$?" && [ $ret -ne 0 ] && return $ret
385+ 
386+ return 0
387+}
388+ 
389+# 移除子包运行版本
390+unset_running_package() {
391+ local dirpath="$1"
392+ local package="$2"
393+ 
394+ unset_package_version "${dirpath}" "${package}" "running"
395+}
396+ 
397+# 获取子包运行版本
398+get_running_package_version() {
399+ local _outvar="$1"
400+ local _dirpath="$2"
401+ local _package="$3"
402+ 
403+ get_package_version_version_cfg "${_outvar}" "${_dirpath}" "${_package}" "running"
404+}
405+ 
406+# 设置子包升级版本
407+set_upgrade_package_version() {
408+ local dirpath="$1"
409+ local package="$2"
410+ local version="$3"
411+ local version_dir="$4"
412+ local filepath filepath_new line cfg_items package_name attr_type upgrade_version
413+ local setted="false" cleanup="" ret mod
414+ 
415+ get_version_cfg_path "filepath" "${dirpath}"
416+ get_version_cfg_new_path "filepath_new" "${dirpath}"
417+ 
418+ check_cfg_version_with_log "${filepath}"
419+ if [ $? -ne 0 ]; then
420+ return 1
421+ fi
422+ 
423+ pack_upgrade_version "upgrade_version" "${package}" "${version}" "${version_dir}"
424+ 
425+ # 保存当前目录的权限
426+ get_file_mod "mod" "${dirpath}"
427+ 
428+ # 添加目录写权限
429+ chmod u+w "${dirpath}"
430+ ret="$?" && [ $ret -ne 0 ] && return $ret
431+ 
432+ # 恢复权限
433+ cleanup="chmod ${mod} \"${dirpath}\""
434+ 
435+ rm -f "${filepath_new}"
436+ if [ $? -ne 0 ]; then
437+ log "ERROR" "delete ${filepath_new} failed!"
438+ eval "${cleanup}"
439+ return 1
440+ fi
441+ 
442+ touch "${filepath}"
443+ while read line; do
444+ parse_cfg_line "cfg_items" "${line}"
445+ [ "${cfg_items}" = "" ] && continue
446+ 
447+ __index_list_by_cut "${cfg_items}" 0 "package_name" 1 "attr_type"
448+ 
449+ if [ "${attr_type}" = "upgrade" ]; then
450+ if [ "${package_name}" = "${package}" ]; then
451+ if [ "${setted}" = "false" ]; then
452+ echo "${upgrade_version}" >> "${filepath_new}"
453+ setted="true"
454+ fi
455+ else
456+ echo "${line}" >> "${filepath_new}"
457+ fi
458+ elif [ "${attr_type}" = "installed" ]; then
459+ if [ "${setted}" = "false" ]; then
460+ echo "${upgrade_version}" >> "${filepath_new}"
461+ setted="true"
462+ fi
463+ echo "${line}" >> "${filepath_new}"
464+ else
465+ echo "${line}" >> "${filepath_new}"
466+ fi
467+ done < "${filepath}"
468+ 
469+ if [ "${setted}" = "false" ]; then
470+ echo "${upgrade_version}" >> "${filepath_new}"
471+ setted="true"
472+ fi
473+ 
474+ add_cfg_version_comment "${filepath_new}"
475+ mv -f "${filepath_new}" "${filepath}"
476+ if [ $? -ne 0 ]; then
477+ log "ERROR" "replace ${filepath} failed!"
478+ eval "${cleanup}"
479+ return 1
480+ fi
481+ 
482+ eval "${cleanup}"
483+ ret="$?" && [ $ret -ne 0 ] && return $ret
484+ 
485+ return 0
486+}
487+ 
488+# 移除子包升级版本
489+unset_upgrade_package() {
490+ local dirpath="$1"
491+ local package="$2"
492+ unset_package_version "${dirpath}" "${package}" "upgrade"
493+}
494+ 
495+# 获取子包升级版本
496+get_upgrade_package_version() {
497+ local _outvar="$1"
498+ local _dirpath="$2"
499+ local _package="$3"
500+ 
501+ get_package_version_version_cfg "${_outvar}" "${_dirpath}" "${_package}" "upgrade"
502+}
503+ 
504+# 包版本是否为某状态
505+is_package_version_status() {
506+ local _outvar="$1"
507+ local _latest_path="$2"
508+ local _package="$3"
509+ local _version="$4"
510+ local _version_dir="$5"
511+ local _target_type="$6"
512+ local _pkg_status_version
513+ local _ret _result_ipvs=""
514+ 
515+ eval "${_outvar}=\"\""
516+ 
517+ get_package_version_version_cfg "_pkg_status_version" "${_latest_path}" "${_package}" "${_target_type}"
518+ _ret="$?" && [ $_ret -ne 0 ] && return $_ret
519+ 
520+ if [ "${_pkg_status_version}" = "${_version}:${_version_dir}" ]; then
521+ _result_ipvs="true"
522+ else
523+ _result_ipvs="false"
524+ fi
525+ 
526+ eval "${_outvar}=\"${_result_ipvs}\""
527+}
528+ 
529+# 包版本是否running
530+is_package_version_running() {
531+ local _outvar="$1"
532+ local _latest_path="$2"
533+ local _package="$3"
534+ local _version="$4"
535+ local _version_dir="$5"
536+ 
537+ is_package_version_status "${_outvar}" "${_latest_path}" "${_package}" "${_version}" "${_version_dir}" "running"
538+}
539+ 
540+# 包版本是否upgrade
541+is_package_version_upgrade() {
542+ local _outvar="$1"
543+ local _latest_path="$2"
544+ local _package="$3"
545+ local _version="$4"
546+ local _version_dir="$5"
547+ 
548+ is_package_version_status "${_outvar}" "${_latest_path}" "${_package}" "${_version}" "${_version_dir}" "upgrade"
549+}
550+ 
551+# 添加安装子包版本
552+add_installed_package_version() {
553+ local dirpath="$1"
554+ local package="$2"
555+ local version="$3"
556+ local version_dir="$4"
557+ local filepath filepath_new line cfg_items package_name attr_type value versions matched version_str
558+ local setted="false"
559+ 
560+ get_version_cfg_path "filepath" "${dirpath}"
561+ get_version_cfg_new_path "filepath_new" "${dirpath}"
562+ 
563+ check_cfg_version_with_log "${filepath}"
564+ if [ $? -ne 0 ]; then
565+ return 1
566+ fi
567+ 
568+ rm -f "${filepath_new}"
569+ if [ $? -ne 0 ]; then
570+ log "ERROR" "delete ${filepath_new} failed!"
571+ return 1
572+ fi
573+ 
574+ pack_versions "version_str" "${version}:${version_dir}"
575+ 
576+ touch "${filepath}"
577+ while read line; do
578+ parse_cfg_line "cfg_items" "${line}"
579+ [ "${cfg_items}" = "" ] && continue
580+ 
581+ __index_list_by_cut "${cfg_items}" 0 "package_name" 1 "attr_type" 2 "value"
582+ 
583+ if [ "${attr_type}" = "installed" ] && [ "${package_name}" = "${package}" ]; then
584+ unpack_versions "versions" "${value}"
585+ __item_in_list "matched" "${version}:${version_dir}" ${versions}
586+ if [ "${matched}" = "true" ]; then
587+ echo "${line}" >> "${filepath_new}"
588+ else
589+ echo "${line}${version_str}" >> "${filepath_new}"
590+ fi
591+ setted="true"
592+ else
593+ echo "${line}" >> "${filepath_new}"
594+ fi
595+ done < "${filepath}"
596+ 
597+ if [ "${setted}" = "false" ]; then
598+ echo "${package}_installed_version=${version_str}" >> "${filepath_new}"
599+ setted="true"
600+ fi
601+ 
602+ add_cfg_version_comment "${filepath_new}"
603+ mv -f "${filepath_new}" "${filepath}"
604+ if [ $? -ne 0 ]; then
605+ log "ERROR" "replace ${filepath} failed!"
606+ return 1
607+ fi
608+}
609+ 
610+# 删除安装子包版本
611+del_installed_package_version() {
612+ local dirpath="$1"
613+ local package="$2"
614+ local version="$3"
615+ local version_dir="$4"
616+ local filepath filepath_new line cfg_items package_name attr_type value versions len_versions
617+ 
618+ get_version_cfg_path "filepath" "${dirpath}"
619+ get_version_cfg_new_path "filepath_new" "${dirpath}"
620+ 
621+ # 版本状态配置文件不存在则正常退出
622+ if [ ! -f "${filepath}" ]; then
623+ return 0
624+ fi
625+ 
626+ check_cfg_version_with_log "${filepath}"
627+ if [ $? -ne 0 ]; then
628+ return 1
629+ fi
630+ 
631+ rm -f "${filepath_new}"
632+ if [ $? -ne 0 ]; then
633+ log "ERROR" "delete ${filepath_new} failed!"
634+ return 1
635+ fi
636+ 
637+ while read line; do
638+ parse_cfg_line "cfg_items" "${line}"
639+ [ "${cfg_items}" = "" ] && continue
640+ 
641+ __index_list_by_cut "${cfg_items}" 0 "package_name" 1 "attr_type" 2 "value"
642+ 
643+ if [ "${attr_type}" = "installed" ] && [ "${package_name}" = "${package}" ]; then
644+ unpack_versions "versions" "${value}"
645+ versions="$(__remove_item_in_list "${version}:${version_dir}" "${versions}")"
646+ __length_list "${versions}" "len_versions"
647+ if [ ${len_versions} -gt 0 ]; then
648+ pack_versions "value" "${versions}"
649+ echo "${package}_installed_version=${value}" >> "${filepath_new}"
650+ fi
651+ else
652+ echo "${line}" >> "${filepath_new}"
653+ fi
654+ done < "${filepath}"
655+ 
656+ if [ -f "${filepath_new}" ]; then
657+ add_cfg_version_comment "${filepath_new}"
658+ mv -f "${filepath_new}" "${filepath}"
659+ if [ $? -ne 0 ]; then
660+ log "ERROR" "replace ${filepath} failed!"
661+ return 1
662+ fi
663+ else
664+ rm -f "${filepath}"
665+ if [ $? -ne 0 ]; then
666+ log "ERROR" "delete ${filepath} failed!"
667+ return 1
668+ fi
669+ fi
670+ return 0
671+}
672+ 
673+# 获取子包版本
674+get_installed_package_versions() {
675+ local _outvar="$1"
676+ local _dirpath="$2"
677+ local _package="$3"
678+ local _filepath _value _new_versions_gipv
679+ 
680+ get_version_cfg_path "_filepath" "${_dirpath}"
681+ 
682+ if [ ! -f "${_filepath}" ]; then
683+ eval "${_outvar}=\"\""
684+ return 0
685+ fi
686+ 
687+ check_cfg_version "${_filepath}"
688+ if [ $? -ne 0 ]; then
689+ eval "${_outvar}=\"\""
690+ return 1
691+ fi
692+ 
693+ _value="$(grep "^${_package}_installed_version=" "${_filepath}" | cut -d= -f2-)"
694+ unpack_versions "_new_versions_gipv" "${_value}"
695+ __reverse_list "_new_versions_gipv" "${_new_versions_gipv}"
696+ 
697+ eval "${_outvar}=\"${_new_versions_gipv}\""
698+}
699+ 
700+# 获取子包最后一次的安装版本
701+get_package_last_installed_version() {
702+ local _outvar="$1"
703+ local _install_path="$2"
704+ local _package="$3"
705+ local _versions_gpliv="" _version_gpliv="" _ret
706+ 
707+ eval "${_outvar}=\"\""
708+ 
709+ get_installed_package_versions "_versions_gpliv" "${_install_path}/${LATEST_DIR}" "${_package}"
710+ _ret="$?" && [ ${_ret} -ne 0 ] && return ${_ret}
711+ 
712+ [ "${_versions_gpliv}" = "" ] && return 0
713+ 
714+ __index_list_by_cut "${_versions_gpliv}" 0 "_version_gpliv"
715+ 
716+ eval "${_outvar}=\"${_version_gpliv}\""
717+}
718+ 
719+ 
720+# 获取子包最后一次的安装版本目录
721+# 如果不存在上次安装版本,则返回空字符串
722+get_package_last_installed_version_dir() {
723+ local _outvar="$1"
724+ local _install_path="$2"
725+ local _package="$3"
726+ local _version_pair _version_pair_arr _version_dir_gplivd="" _ret
727+ 
728+ eval "${_outvar}=\"\""
729+ 
730+ get_package_last_installed_version "_version_pair" "${_install_path}" "${_package}"
731+ _ret="$?" && [ ${_ret} -ne 0 ] && return ${_ret}
732+ 
733+ unpack_version_pair "_version_pair_arr" "${_version_pair}"
734+ __index_list_by_cut "${_version_pair_arr}" 1 "_version_dir_gplivd"
735+ 
736+ eval "${_outvar}=\"${_version_dir_gplivd}\""
737+}
738+ 
739+# 获取子包最后一次安装的install.info路径
740+# 如果不存在上次安装版本,则返回空字符串
741+get_package_last_install_info() {
742+ local _outvar="$1"
743+ local _install_path="$2"
744+ local _package="$3"
745+ local _version_dir="" _install_info="" _ret
746+ 
747+ eval "${_outvar}=\"\""
748+ 
749+ get_package_last_installed_version_dir "_version_dir" "${_install_path}" "${_package}"
750+ _ret="$?" && [ ${_ret} -ne 0 ] && return ${_ret}
751+ 
752+ [ "${_version_dir}" = "" ] && return 0
753+ 
754+ get_package_install_info "_install_info" "${_install_path}" "${_version_dir}" "${_package}"
755+ 
756+ eval "${_outvar}=\"${_install_info}\""
757+}
758+ 
759+get_package_version_dir() {
760+ local _outvar="$1"
761+ local _install_path="$2"
762+ local _package="$3"
763+ local _target_type="$4"
764+ local _version_pair _version_pair_arr _version_dir_gprvd="" _ret
765+ 
766+ eval "${_outvar}=\"\""
767+ 
768+ get_package_version_version_cfg "_version_pair" "${_install_path}/${LATEST_DIR}" "${_package}" "${_target_type}"
769+ _ret="$?" && [ ${_ret} -ne 0 ] && return ${_ret}
770+ 
771+ if [ "${_version_pair}" = "" ] && [ "${_target_type}" = "upgrade" ]; then
772+ get_package_version_version_cfg "_version_pair" "${_install_path}/${LATEST_DIR}" "${_package}" "running"
773+ _ret="$?" && [ ${_ret} -ne 0 ] && return ${_ret}
774+ fi
775+ 
776+ [ "${_version_pair}" = "" ] && return 0
777+ 
778+ unpack_version_pair "_version_pair_arr" "${_version_pair}"
779+ __index_list_by_cut "${_version_pair_arr}" 1 "_version_dir_gprvd"
780+ 
781+ eval "${_outvar}=\"${_version_dir_gprvd}\""
782+}
783+ 
784+get_package_install_message() {
785+ local _outvar="$1"
786+ local _install_path="$2"
787+ local _package="$3"
788+ local _target_type="$4"
789+ local _version_dir_gpim="" _install_info_gpim="" _ret
790+ 
791+ eval "${_outvar}=\"\""
792+ 
793+ get_package_version_dir "_version_dir_gpim" "${_install_path}" "${_package}" "${_target_type}"
794+ _ret="$?" && [ ${_ret} -ne 0 ] && return ${_ret}
795+ 
796+ [ "${_version_dir_gpim}" = "" ] && return 0
797+ 
798+ get_package_install_info "_install_info_gpim" "${_install_path}" "${_version_dir_gpim}" "${_package}"
799+ 
800+ eval "${_outvar}=\"${_install_info_gpim}\""
801+}
802+ 
803+# 获取子包的运行版本目录
804+# 如果不存在运行版本,则返回空字符串
805+get_package_running_version_dir() {
806+ local _outvar="$1"
807+ local _install_path="$2"
808+ local _package="$3"
809+ 
810+ get_package_version_dir "${_outvar}" "${_install_path}" "${_package}" "running"
811+}
812+ 
813+# 获取子包运行版本的install.info路径
814+# 如果不存在运行版本,则返回空字符串
815+get_package_running_install_info() {
816+ local _outvar="$1"
817+ local _install_path="$2"
818+ local _package="$3"
819+ 
820+ get_package_install_message "${_outvar}" "${_install_path}" "${_package}" "running"
821+}
822+ 
823+# 获取子包的升级版本目录
824+# 如果不存在升级版本,则返回运行版本目录
825+# 如果也不存在运行版本,则返回空字符串
826+get_package_upgrade_version_dir() {
827+ local _outvar="$1"
828+ local _install_path="$2"
829+ local _package="$3"
830+ 
831+ get_package_version_dir "${_outvar}" "${_install_path}" "${_package}" "upgrade"
832+}
833+ 
834+# 获取子包升级版本的install.info路径
835+# 如果不存在升级版本,则返回运行版本的install.info路径
836+# 如果也不存在运行版本,则返回空字符串
837+get_package_upgrade_install_info() {
838+ local _outvar="$1"
839+ local _install_path="$2"
840+ local _package="$3"
841+ 
842+ get_package_install_message "${_outvar}" "${_install_path}" "${_package}" "upgrade"
843+}
844+ 
845+# 获取所有运行包
846+get_running_packages() {
847+ local _outvar="$1"
848+ local _dirpath="$2"
849+ local _filepath _line _cfg_items _package_name_grp _attr_type_grp _packages=""
850+ 
851+ eval "${_outvar}=\"\""
852+ 
853+ get_version_cfg_path "_filepath" "${_dirpath}"
854+ 
855+ if [ ! -f "${_filepath}" ]; then
856+ return 0
857+ fi
858+ 
859+ check_cfg_version "${_filepath}"
860+ if [ $? -ne 0 ]; then
861+ return 1
862+ fi
863+ 
864+ while read _line; do
865+ parse_cfg_line "_cfg_items" "${_line}"
866+ [ "${_cfg_items}" = "" ] && continue
867+ 
868+ __index_list_by_cut "${_cfg_items}" 0 "_package_name_grp" 1 "_attr_type_grp"
869+ 
870+ if [ "${_attr_type_grp}" = "running" ]; then
871+ if [ "${_packages}" = "" ]; then
872+ _packages="${_package_name_grp}"
873+ else
874+ _packages="${_packages} ${_package_name_grp}"
875+ fi
876+ fi
877+ done < "${_filepath}"
878+ 
879+ eval "${_outvar}=\"${_packages}\""
880+}
881+ 
882+# 根据版本目录获取版本号
883+get_version_by_version_dir() {
884+ local _outvar="$1"
885+ local _dirpath="$2"
886+ local _version_dir="$3"
887+ local _filepath _line _cfg_items _attr_type_gvbvd _value_gvbvd
888+ local _version_pairs _version_pair _version_pair_arr _version_gvbvd _version_dir_gvbvd
889+ 
890+ eval "${_outvar}=\"\""
891+ 
892+ get_version_cfg_path "_filepath" "${_dirpath}"
893+ if [ ! -f "${_filepath}" ]; then
894+ return 1
895+ fi
896+ 
897+ check_cfg_version "${_filepath}"
898+ if [ $? -ne 0 ]; then
899+ return 1
900+ fi
901+ 
902+ while read _line; do
903+ parse_cfg_line "_cfg_items" "${_line}"
904+ [ "${_cfg_items}" = "" ] && continue
905+ 
906+ __index_list_by_cut "${_cfg_items}" 1 "_attr_type_gvbvd" 2 "_value_gvbvd"
907+ 
908+ if [ "${_attr_type_gvbvd}" = "installed" ]; then
909+ unpack_versions "_version_pairs" "${_value_gvbvd}"
910+ for _version_pair in ${_version_pairs}; do
911+ unpack_version_pair "_version_pair_arr" "${_version_pair}"
912+ __index_list_by_cut "${_version_pair_arr}" 0 "_version_gvbvd" 1 "_version_dir_gvbvd"
913+ if [ "${_version_dir_gvbvd}" = "${_version_dir}" ]; then
914+ eval "${_outvar}=\"${_version_gvbvd}\""
915+ return 0
916+ fi
917+ done
918+ fi
919+ done < "${_filepath}"
920+ 
921+ return 1
922+}
923+ 
924+# 根据版本号与版本目录获取所有安装包
925+get_installed_packages_by_version_version_dir() {
926+ local _outvar="$1"
927+ local _dirpath="$2"
928+ local _version="$3"
929+ local _version_dir="$4"
930+ local _filepath _line _cfg_items
931+ local _package_gipbvvd _attr_type_gipbvvd _value_gipbvvd _version_pairs _in_list
932+ local _packages_gipbvvd=""
933+ 
934+ eval "${_outvar}=\"\""
935+ 
936+ get_version_cfg_path "_filepath" "${_dirpath}"
937+ if [ ! -f "${_filepath}" ]; then
938+ return 1
939+ fi
940+ 
941+ check_cfg_version "${_filepath}"
942+ if [ $? -ne 0 ]; then
943+ return 1
944+ fi
945+ 
946+ while read _line; do
947+ parse_cfg_line "_cfg_items" "${_line}"
948+ [ "${_cfg_items}" = "" ] && continue
949+ 
950+ __index_list_by_cut "${_cfg_items}" 0 "_package_gipbvvd" 1 "_attr_type_gipbvvd" 2 "_value_gipbvvd"
951+ 
952+ if [ "${_attr_type_gipbvvd}" = "installed" ]; then
953+ unpack_versions "_version_pairs" "${_value_gipbvvd}"
954+ __item_in_list "_in_list" "${_version}:${_version_dir}" ${_version_pairs}
955+ if [ "${_in_list}" = "true" ]; then
956+ if [ "${_packages_gipbvvd}" = "" ]; then
957+ _packages_gipbvvd="${_package_gipbvvd}"
958+ else
959+ _packages_gipbvvd="${_packages_gipbvvd} ${_package_gipbvvd}"
960+ fi
961+ fi
962+ fi
963+ done < "${_filepath}"
964+ 
965+ eval "${_outvar}=\"${_packages_gipbvvd}\""
966+}
967+ 
968+# 获取所有安装包
969+get_installed_packages() {
970+ local _outvar="$1"
971+ local _dirpath="$2"
972+ local _filepath _line _cfg_items _package_name_gip _attr_type_gip _packages=""
973+ 
974+ eval "${_outvar}=\"\""
975+ 
976+ get_version_cfg_path "_filepath" "${_dirpath}"
977+ 
978+ if [ ! -f "${_filepath}" ]; then
979+ return 0
980+ fi
981+ 
982+ check_cfg_version "${_filepath}"
983+ if [ $? -ne 0 ]; then
984+ return 1
985+ fi
986+ 
987+ while read _line; do
988+ parse_cfg_line "_cfg_items" "${_line}"
989+ [ "${_cfg_items}" = "" ] && continue
990+ 
991+ __index_list_by_cut "${_cfg_items}" 0 "_package_name_gip" 1 "_attr_type_gip"
992+ 
993+ if [ "${_attr_type_gip}" = "installed" ]; then
994+ if [ "${_packages}" = "" ]; then
995+ _packages="${_package_name_gip}"
996+ else
997+ _packages="${_packages} ${_package_name_gip}"
998+ fi
999+ fi
1000+ done < "${_filepath}"
1001+ 
1002+ eval "${_outvar}=\"${_packages}\""
1003+}
1004+ 
1005+# version.cfg文件是否存在
1006+# 返回值0为真,1为假
1007+version_cfg_exists() {
1008+ local dirpath="$1"
1009+ local filepath
1010+ 
1011+ get_version_cfg_path "filepath" "$dirpath"
1012+ if [ -f "$filepath" ]; then
1013+ return 0
1014+ fi
1015+ return 1
1016+}
1017+ 
1018+# 显示version.cfg文件
1019+show_version_cfg() {
1020+ local dirpath="$1"
1021+ local filepath
1022+ 
1023+ get_version_cfg_path "filepath" "$dirpath"
1024+ if [ -f "$filepath" ]; then
1025+ cat "$filepath"
1026+ fi
1027+}
Ascripts/package/common/sh/version_compatiable.inc+172-0
@@ -0,0 +1,172 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+# 获取版本号,用于版本兼容性对比
13+get_package_compat_version() {
14+ local _outvar="$1"
15+ local _version_info_path="$2"
16+ local _result
17+ 
18+ if [ ! -f "${_version_info_path}" ]; then
19+ read -r "$_outvar" <<EOF
20+EOF
21+ return 1
22+ fi
23+ 
24+ read -r "$_outvar" <<EOF
25+$(grep "^Version=" "$_version_info_path" | cut -d= -f2- | cut -d- -f1 | cut -d. -f-3)
26+EOF
27+}
28+ 
29+# 获取正式包名
30+get_formal_package_name() {
31+ local _outvar="$1"
32+ local _package="$2"
33+ read -r "$_outvar" <<EOF
34+$(echo "$_package" | sed 's/_/-/g')
35+EOF
36+}
37+ 
38+# 获取非正式包名
39+get_informal_package_name() {
40+ local _outvar="$1"
41+ local _package="$2"
42+ read -r "$_outvar" <<EOF
43+$(echo "$_package" | sed 's/-/_/g')
44+EOF
45+}
46+ 
47+# 获取需求包列表。
48+_get_required_packages() {
49+ local _version_info_path="$1"
50+ 
51+ if [ ! -f "$_version_info_path" ]; then
52+ return 0
53+ fi
54+ 
55+ awk '
56+ function join(items, len, sep, result, i) {
57+ if (len >= 1) {
58+ result = items[1]
59+ for (i = 2; i <= len; i++) {
60+ result = sprintf("%s%s%s", result, sep, items[i])
61+ }
62+ return result
63+ }
64+ return ""
65+ }
66+ 
67+ /^required_package_.+_version=/ {
68+ split($0, line_tokens, "=")
69+ len_require_tokens = split(line_tokens[1], require_tokens, "_")
70+ len_package_names = 0
71+ for (i = 3; i < len_require_tokens; i++) {
72+ len_package_names++
73+ package_names[len_package_names] = require_tokens[i]
74+ }
75+ package_name = join(package_names, len_package_names, "_")
76+ print package_name
77+ }
78+ ' "${_version_info_path}"
79+}
80+ 
81+# 获取需求包信息。
82+_get_required_package_info() {
83+ local _outvar="$1"
84+ local _version_info_path="$2"
85+ local _pkg_name="$3"
86+ local _required_pkg_name
87+ local _required_value
88+ 
89+ if [ ! -f "${_version_info_path}" ]; then
90+ eval "${_outvar}=\"\""
91+ return 1
92+ fi
93+ 
94+ _required_pkg_name="required_package_${_pkg_name}_version"
95+ _required_value="$(grep "^${_required_pkg_name}=" "${_version_info_path}" | cut -d= -f2-)"
96+ # _required_value取得的值带有双引号,eval时不可以在外侧再添加双引号
97+ 
98+ eval "${_outvar}=${_required_value}"
99+}
100+ 
101+# 检查版本与需求版本兼容性。
102+# 兼容返回0,不兼容返回1
103+_check_version_required() {
104+ local version="$1"
105+ local require="$2"
106+ local script_dir="$3"
107+ local src_pkg="$4"
108+ local dst_pkg="$5"
109+ local result
110+ 
111+ result=$(awk -f "${script_dir}/check_version_required.awk" -v version="${version}" -v all_required="${require}")
112+ if [ "${result}" = "T" ]; then
113+ return 0
114+ fi
115+ 
116+ echo "Version compatibility check failed, $src_pkg required $dst_pkg version $require, but $dst_pkg version is $version!"
117+ return 1
118+}
119+ 
120+# 检查版本兼容性。
121+_check_version_compatiable() {
122+ local package="$1"
123+ local install_path="$2"
124+ local script_dir="$3"
125+ local version_info_path="$script_dir/../version.info"
126+ local err_msgs package_formal self_version src_pkg src_pkg_formal dst_pkg dst_pkg_formal
127+ local installed_version_info pkg_version_info_path
128+ 
129+ get_formal_package_name "package_formal" "$package"
130+ get_package_compat_version "self_version" "$version_info_path"
131+ 
132+ if [ -d "$install_path/share/info" ]; then
133+ err_msgs="$(
134+ ls "$install_path/share/info" | grep -v "^${package}$" | while read src_pkg; do
135+ get_formal_package_name "src_pkg_formal" "$src_pkg"
136+ installed_version_info="$install_path/share/info/$src_pkg/version.info"
137+ grep "^required_package_${package_formal}_version=" "$installed_version_info" | cut -d= -f2- | tr -d '"' | while read required; do
138+ _check_version_required "$self_version" "$required" "$script_dir" "$src_pkg_formal" "$package_formal"
139+ done
140+ done
141+ )"
142+ if [ "$err_msgs" != "" ]; then
143+ while read err_msg; do
144+ comm_log "WARNING" "$err_msg"
145+ done <<EOF
146+$err_msgs
147+EOF
148+ fi
149+ fi
150+ err_msgs="$(
151+ _get_required_packages "${version_info_path}" | while read dst_pkg_formal; do
152+ get_informal_package_name "dst_pkg" "$dst_pkg_formal"
153+ pkg_version_info_path="$install_path/share/info/$dst_pkg_formal/version.info"
154+ if [ ! -f "$pkg_version_info_path" ]; then
155+ pkg_version_info_path="$install_path/share/info/$dst_pkg/version.info"
156+ fi
157+ if [ -f "$pkg_version_info_path" ]; then
158+ get_package_compat_version "pkg_version" "$pkg_version_info_path"
159+ _get_required_package_info "required" "$version_info_path" "$dst_pkg_formal"
160+ _check_version_required "$pkg_version" "$required" "$script_dir" "$package_formal" "$dst_pkg_formal"
161+ fi
162+ done
163+ )"
164+ if [ "$err_msgs" != "" ]; then
165+ while read err_msg; do
166+ comm_log "WARNING" "$err_msg"
167+ done <<EOF
168+$err_msgs
169+EOF
170+ fi
171+ return 0
172+}
Ascripts/package/latest_manager/scripts/filelist.csv+16-0
@@ -0,0 +1,16 @@
1+module,operation,relative_path_in_pkg,relative_install_path,is_in_docker,permission,owner:group,install_type,softlink,feature,is_common_path,configurable,hash,block,pkg_inner_softlink,chip
2+NA,mkdir,NA,manager,TRUE,550,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
3+NA,copy,latest_manager/filelist.csv,manager/filelist.csv,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
4+NA,copy,latest_manager/install.sh,manager/install.sh,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
5+NA,copy,latest_manager/manager.sh,manager.sh,TRUE,550,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
6+NA,copy,latest_manager/manager_func.sh,manager/manager_func.sh,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
7+NA,copy,latest_manager/script_operator.inc,manager/script_operator.inc,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
8+NA,copy,latest_manager/uninstall.sh,manager/uninstall.sh,TRUE,550,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
9+NA,copy,latest_manager/version.info,manager/version.info,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
10+NA,copy,latest_manager/version_compatiable.inc,manager/version_compatiable.inc,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
11+NA,copy,latest_manager/check_version_required.awk,manager/check_version_required.awk,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
12+NA,copy,latest_manager/common_func_v2.inc,manager/common_func_v2.inc,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
13+NA,copy,latest_manager/common_func.inc,manager/common_func.inc,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
14+NA,copy,latest_manager/common_installer.inc,manager/common_installer.inc,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
15+NA,copy,latest_manager/install_common_parser.sh,manager/install_common_parser.sh,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
16+NA,copy,latest_manager/version_cfg.inc,manager/version_cfg.inc,TRUE,440,\\$username:\\$usergroup,all,NA,all,N,FALSE,NA,latest_manager,NA,all
Ascripts/package/latest_manager/scripts/install.sh+59-0
@@ -0,0 +1,59 @@
1+#!/bin/bash
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+CURPATH=$(dirname $(readlink -f "$0"))
13+USERNAME=$(id -un)
14+USERGROUP=$(id -gn)
15+common_func_path="${CURPATH}/common_func.inc"
16+ 
17+. "$common_func_path"
18+ 
19+INSTALL_PATH=""
20+IS_UPGRADE="n"
21+ 
22+set_comm_log "Latest_manager" "$COMM_LOGFILE"
23+ 
24+while true
25+do
26+ case "$1" in
27+ --install-path=*)
28+ INSTALL_PATH="$(echo "$1" | cut -d"=" -f2-)"
29+ shift
30+ ;;
31+ --upgrade)
32+ IS_UPGRADE="y"
33+ shift
34+ ;;
35+ -*)
36+ comm_log "ERROR" "Unsupported parameters : $1"
37+ exit 1
38+ ;;
39+ *)
40+ break
41+ ;;
42+ esac
43+done
44+ 
45+if [ "$INSTALL_PATH" = "" ]; then
46+ comm_log "ERROR" "--install-path parameter is required!"
47+ exit 1
48+fi
49+ 
50+if ! sh "$CURPATH/install_common_parser.sh" --package="latest_manager" --install --username="$USERNAME" --usergroup="$USERGROUP" \
51+ --simple-install "full" "$INSTALL_PATH" "$CURPATH/filelist.csv" "all"; then
52+ comm_log "ERROR" "install failed!"
53+ exit 1
54+fi
55+ 
56+if [ "$IS_UPGRADE" = "y" ] && ! "$INSTALL_PATH/manager.sh" "migrate_latest_data"; then
57+ comm_log "ERROR" "migrate latest data failed!"
58+ exit 1
59+fi
Ascripts/package/latest_manager/scripts/manager.sh+128-0
@@ -0,0 +1,128 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+CURPATH=$(dirname "$(readlink -f "$0")")
13+VAR_PATH="$CURPATH"
14+common_func_path="$CURPATH/manager/common_func.inc"
15+version_compatiable_path="$CURPATH/manager/version_compatiable.inc"
16+common_func_v2_path="$CURPATH/manager/common_func_v2.inc"
17+version_cfg_path="$CURPATH/manager/version_cfg.inc"
18+script_operator_path="$CURPATH/manager/script_operator.inc"
19+manager_func_path="$CURPATH/manager/manager_func.sh"
20+ 
21+. "$common_func_path"
22+. "$version_compatiable_path"
23+. "$common_func_v2_path"
24+. "$version_cfg_path"
25+. "$script_operator_path"
26+. "$manager_func_path"
27+ 
28+set_comm_log "Latest_manager" "$COMM_LOGFILE"
29+ 
30+VERSION=""
31+VERSION_DIR=""
32+PACKAGE=""
33+DOCKER_ROOT=""
34+INTPUT_INSTALL_FOR_ALL="n"
35+IS_RECREATE_SOFTLINK="n"
36+INCREMENT="n"
37+USE_SHARE_INFO="n"
38+ 
39+while true
40+do
41+ case "$1" in
42+ --version)
43+ VERSION="$2"
44+ shift 2
45+ ;;
46+ --version-dir)
47+ VERSION_DIR="$2"
48+ shift 2
49+ ;;
50+ --package)
51+ PACKAGE="$2"
52+ shift 2
53+ ;;
54+ --package-dir)
55+ shift 2
56+ ;;
57+ --serial)
58+ shift 2
59+ ;;
60+ --install-type)
61+ shift 2
62+ ;;
63+ --feature)
64+ shift 2
65+ ;;
66+ --chip)
67+ shift 2
68+ ;;
69+ --filelist)
70+ shift 2
71+ ;;
72+ --docker-root)
73+ DOCKER_ROOT="$2"
74+ shift 2
75+ ;;
76+ --recreate-softlink)
77+ IS_RECREATE_SOFTLINK="y"
78+ shift 1
79+ ;;
80+ --install-for-all)
81+ INTPUT_INSTALL_FOR_ALL="y"
82+ shift 1
83+ ;;
84+ --increment)
85+ INCREMENT="y"
86+ shift 1
87+ ;;
88+ --use-share-info)
89+ USE_SHARE_INFO="y"
90+ shift 1
91+ ;;
92+ -*)
93+ comm_log "ERROR" "Unsupported parameters : $1"
94+ exit 1
95+ ;;
96+ *)
97+ break
98+ ;;
99+ esac
100+done
101+ 
102+if [ "$PACKAGE" != "" ]; then
103+ get_titled_package_name "TITLED_PACKAGE" "$PACKAGE"
104+ set_comm_log "$TITLED_PACKAGE" "$COMM_LOGFILE"
105+fi
106+ 
107+OPERATION="$1"
108+ 
109+if [ "$OPERATION" = "package_installed" ]; then
110+ package_installed "$CURPATH" "$VERSION" "$VERSION_DIR" "$PACKAGE" \
111+ "$INTPUT_INSTALL_FOR_ALL" "$DOCKER_ROOT"
112+elif [ "$OPERATION" = "package_pre_uninstall" ]; then
113+ package_pre_uninstall "$CURPATH" "$VERSION" "$VERSION_DIR" "$PACKAGE" \
114+ "$INTPUT_INSTALL_FOR_ALL" "$DOCKER_ROOT"
115+elif [ "$OPERATION" = "package_uninstalled" ]; then
116+ package_uninstalled "$CURPATH" "$VERSION" "$VERSION_DIR" "$PACKAGE" "$IS_RECREATE_SOFTLINK" \
117+ "$DOCKER_ROOT"
118+elif [ "$OPERATION" = "package_create_softlink" ]; then
119+ package_create_softlink "$CURPATH" "$VERSION" "$VERSION_DIR" "$PACKAGE" "$DOCKER_ROOT"
120+elif [ "$OPERATION" = "package_remove_softlink" ]; then
121+ package_remove_softlink "$CURPATH" "$VERSION" "$VERSION_DIR" "$PACKAGE" "$DOCKER_ROOT"
122+elif [ "$OPERATION" = "create_version_softlink" ]; then
123+ create_version_softlink "$CURPATH" "$VERSION_DIR"
124+elif [ "$OPERATION" = "remove_latest_softlink" ]; then
125+ remove_latest_softlink "$CURPATH"
126+elif [ "$OPERATION" = "migrate_latest_data" ]; then
127+ migrate_latest_data "$CURPATH"
128+fi
Ascripts/package/latest_manager/scripts/manager_func.sh+1781-0
Ascripts/package/latest_manager/scripts/uninstall.sh+52-0
@@ -0,0 +1,52 @@
1+#!/bin/bash
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+CURPATH=$(dirname $(readlink -f "$0"))
13+VARPATH="$(dirname "$CURPATH")"
14+USERNAME=$(id -un)
15+USERGROUP=$(id -gn)
16+common_func_path="$CURPATH/common_func.inc"
17+manager_func_path="$CURPATH/manager_func.sh"
18+ 
19+. "$common_func_path"
20+. "$manager_func_path"
21+ 
22+set_comm_log "Latest_manager" "$COMM_LOGFILE"
23+ 
24+IS_UPGRADE="n"
25+ 
26+while true
27+do
28+ case "$1" in
29+ --upgrade)
30+ IS_UPGRADE="y"
31+ shift
32+ ;;
33+ *)
34+ break
35+ ;;
36+ esac
37+done
38+ 
39+if ! sh "$CURPATH/install_common_parser.sh" --package="latest_manager" --uninstall --username="$USERNAME" --usergroup="$USERGROUP" \
40+ --simple-uninstall "full" "$VARPATH" "$CURPATH/filelist.csv" "all"; then
41+ comm_log "ERROR" "uninstall failed!"
42+ exit 1
43+fi
44+ 
45+if [ "$IS_UPGRADE" = "n" ]; then
46+ remove_manager_refs "$VARPATH"
47+fi
48+ 
49+if ! remove_dir_if_empty "$VARPATH"; then
50+ comm_log "ERROR" "uninstall failed!"
51+ exit 1
52+fi
Ascripts/package/module/ascend/EngineeringCommon.xml+14-0
@@ -0,0 +1,14 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<block name="EngineeringCommon" install_own="$username:$usergroup" install_mod="440" install_type="all">
3+ <dir_info value="common" install_mod="550">
4+ <path value="$(TARGET_ENV)"/>
5+ <path value="$(TARGET_ENV)/bin" install_softlink="bin"/>
6+ <path value="$(TARGET_ENV)/include" install_softlink="include"/>
7+ <path value="$(TARGET_ENV)/include/version" install_type="devel"/>
8+ <path value="$(TARGET_ENV)/lib64" install_softlink="lib64"/>
9+ <path value="$(TARGET_ENV)/devlib" install_softlink="devlib"/>
10+ <path value="$(TARGET_ENV)/conf" install_softlink="conf"/>
11+ <path value="$(TARGET_ENV)/pkg_inc" install_softlink="pkg_inc"/>
12+ <path value="var" install_mod="750"/>
13+ </dir_info>
14+</block>
Ascripts/package/module/ascend/EngineeringFiles.xml+6-0
@@ -0,0 +1,6 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<block name="EngineeringFiles" install_own="$username:$usergroup" install_mod="440" install_type="all">
3+ <file_info value="conf" copy_type="source" src_path="build/release/config/common" dst_path="conf" install_path="$(TARGET_ENV)/conf">
4+ <file value="path.cfg"/>
5+ </file_info>
6+</block>
Ascripts/package/module/ascend/OpsFft.xml+9-0
@@ -0,0 +1,9 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<block name="OpsFft" copy_type="delivery" src_path="" dst_path="" install_path="" install_own="$username:$usergroup" install_mod="550" install_type="run;devel">
3+ <dir_info install_mod="550">
4+ <path value="$(TARGET_ENV)/lib64"/>
5+ </dir_info>
6+ <file_info value="ops_tensor_lib" copy_type="delivery" src_path="" dst_path="lib64/" install_path="$(TARGET_ENV)/lib64/">
7+ <file value="libcann_ops_tensor.so" install_mod="550"/>
8+ </file_info>
9+</block>
Ascripts/package/module/ascend/OpsFftInc.xml+9-0
@@ -0,0 +1,9 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<block name="OpsFftInc" copy_type="delivery" src_path="" dst_path="" install_path="" install_own="$username:$usergroup" install_mod="550" install_type="devel">
3+ <dir_info install_mod="550">
4+ <path value="$(TARGET_ENV)/include"/>
5+ </dir_info>
6+ <file_info value="ops_tensor_inc" copy_type="delivery" src_path="" dst_path="include/" install_path="$(TARGET_ENV)/include/">
7+ <file value="cann_ops_tensor.h" install_mod="550"/>
8+ </file_info>
9+</block>
Ascripts/package/ops_fft/ops_fft.xml+37-0
@@ -0,0 +1,37 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<config name="ops_fft" copy_type="delivery" src_path="lib/" dst_path="ops_fft" install_type="all" install_mod="550" install_own="$username:$usergroup">
3+ <package_info>
4+ <product_name>cann</product_name>
5+ <chip_name>950</chip_name>
6+ <func_name>ops-fft</func_name>
7+ <suffix>run</suffix>
8+ <install_script>share/info/ops_fft/script/install.sh</install_script>
9+ <help>share/info/ops_fft/script/help.info</help>
10+ <cleanup>share/info/ops_fft/script/cleanup.sh</cleanup>
11+ </package_info>
12+ <generate_info value="ops_fft_version.h" dst_path="share/info/ops_fft" install_path="$(TARGET_ENV)/include/version" install_mod="440" install_type="devel" generator="version_header">
13+ <OPS_FFT_VERSION>$(ASCEND_VER)</OPS_FFT_VERSION>
14+ <OPS_FFT_TIMESTAMP>$(TIMESTAMP_NO)</OPS_FFT_TIMESTAMP>
15+ </generate_info>
16+ <generate_info value="scene.info" dst_path="share/info/ops_fft" install_path="share/info/ops_fft" install_mod="440">
17+ <os>$(OS_NAME)</os>
18+ <os_version>$(OS_VER)</os_version>
19+ <arch>$(ARCH)</arch>
20+ </generate_info>
21+ <file_info value="ops_fft_script" copy_type="source" src_path="" dst_path="share/info/ops_fft" install_path="share/info/ops_fft">
22+ <file value="script" entity="true"/>
23+ <file value="version.info"/>
24+ </file_info>
25+ <block_info dependtree="false" dst_path="ops_fft" block_conf_path="ascend">
26+ <block name="EngineeringCommon"/>
27+ <block name="EngineeringFiles"/>
28+ <block name="OpsFft"/>
29+ <block name="OpsFftInc"/>
30+ </block_info>
31+ <dir_info value="ops_fft" install_path="ops_fft" install_type="all">
32+ <path value="share" install_mod="750"/>
33+ <path value="share/info" install_mod="750"/>
34+ <path value="share/info/ops_fft" install_mod="550"/>
35+ <path value="share/info/ops_fft/script" install_mod="550"/>
36+ </dir_info>
37+</config>
Ascripts/package/ops_fft/scripts/cleanup.sh+14-0
@@ -0,0 +1,14 @@
1+#!/bin/sh
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+set -e
13+ 
14+rm -rf $(pwd)
Ascripts/package/ops_fft/scripts/help.info+10-0
@@ -0,0 +1,10 @@
1+ --full Install full mode
2+ --install-path=<path> Install product to specific dir path
3+ --install-for-all Install for all user
4+ --quiet Quiet install mode, skip human-computer interactions
5+ --uninstall Uninstall product with compatible run package which is installed before
6+ --install-path=<path> Uninstall specific ops_fft dir path
7+ --upgrade Upgrade product immediately
8+ --install-path=<path> Upgrade specific ops_fft dir path
9+ --install-for-all Install for all user
10+ --quiet Quiet install mode, skip human-computer interactions
Ascripts/package/ops_fft/scripts/install.sh+565-0
@@ -0,0 +1,565 @@
1+#!/bin/bash
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+# ops-fft Installation Script (Built-in Package)
12+# ----------------------------------------------------------------------------
13+ 
14+# ==================== Error Codes ====================
15+PARAM_INVALID="0x0002"
16+FILE_NOT_EXIST="0x0080"
17+PERM_DENIED="0x0093"
18+PERM_DENIED_DES="Permission denied."
19+ 
20+# ==================== Global Variables ====================
21+OPP_PLATFORM_DIR="ops_fft"
22+OPP_PLATFORM_UPPER=$(echo "${OPP_PLATFORM_DIR}" | tr '[:lower:]' '[:upper:]')
23+CURR_OPERATE_USER="$(id -nu 2>/dev/null)"
24+CURR_OPERATE_GROUP="$(id -ng 2>/dev/null)"
25+ 
26+# Defaults for general user
27+if [ "$(id -u)" != "0" ]; then
28+ DEFAULT_INSTALL_PATH="${HOME}/Ascend"
29+else
30+ IS_FOR_ALL="y"
31+ DEFAULT_INSTALL_PATH="/usr/local/Ascend"
32+fi
33+ 
34+# Run package's files info
35+CURR_PATH=$(dirname "$(readlink -f "$0")")
36+RUN_PKG_INFO_FILE="${CURR_PATH}/../scene.info"
37+VERSION_INFO_FILE="${CURR_PATH}/../version.info"
38+COMMON_INC_FILE="${CURR_PATH}/common_func.inc"
39+COMMON_FUNC_V2_PATH="${CURR_PATH}/common_func_v2.inc"
40+VERSION_CFG_PATH="${CURR_PATH}/version_cfg.inc"
41+COMMON_PARSER_FILE="${CURR_PATH}/install_common_parser.sh"
42+INSTALL_SHELL_FILE="${CURR_PATH}/opp_install.sh"
43+UNINSTALL_SHELL_FILE="${CURR_PATH}/opp_uninstall.sh"
44+OPP_COMMON_FILE="${CURR_PATH}/opp_common.sh"
45+ 
46+# Source common functions if available
47+if [ -f "${OPP_COMMON_FILE}" ]; then
48+ . "${OPP_COMMON_FILE}"
49+fi
50+if [ -f "${COMMON_INC_FILE}" ]; then
51+ . "${COMMON_INC_FILE}"
52+fi
53+if [ -f "${COMMON_FUNC_V2_PATH}" ]; then
54+ . "${COMMON_FUNC_V2_PATH}"
55+fi
56+if [ -f "${VERSION_CFG_PATH}" ]; then
57+ . "${VERSION_CFG_PATH}"
58+fi
59+ 
60+# Get architecture info from scene.info if available
61+if [ -f "${RUN_PKG_INFO_FILE}" ]; then
62+ ARCH_INFO=$(grep -e "arch" "$RUN_PKG_INFO_FILE" 2>/dev/null | cut --only-delimited -d"=" -f2-)
63+else
64+ ARCH_INFO=$(uname -m)
65+fi
66+ 
67+# Installation info
68+ASCEND_INSTALL_INFO="ascend_install.info"
69+TARGET_INSTALL_PATH="${DEFAULT_INSTALL_PATH}"
70+TARGET_USERNAME="${CURR_OPERATE_USER}"
71+TARGET_USERGROUP="${CURR_OPERATE_GROUP}"
72+TARGET_VERSION_DIR=""
73+TARGET_SHARED_INFO_DIR=""
74+ 
75+# Keys for ascend_install.info
76+KEY_INSTALLED_UNAME="USERNAME"
77+KEY_INSTALLED_UGROUP="USERGROUP"
78+KEY_INSTALLED_TYPE="${OPP_PLATFORM_UPPER}_INSTALL_TYPE"
79+KEY_INSTALLED_PATH="${OPP_PLATFORM_UPPER}_INSTALL_PATH_VAL"
80+KEY_INSTALLED_VERSION="${OPP_PLATFORM_UPPER}_VERSION"
81+KEY_INSTALLED_FEATURE="${OPP_PLATFORM_UPPER}_INSTALL_FEATURE"
82+KEY_INSTALLED_CHIP="${OPP_PLATFORM_UPPER}_INSTALL_CHIP"
83+ 
84+# Keys for run package
85+KEY_RUNPKG_VERSION="Version"
86+ 
87+# Command line options
88+CMD_LIST="$*"
89+IS_UNINSTALL=n
90+IS_INSTALL=n
91+IS_UPGRADE=n
92+IS_QUIET=n
93+IS_INPUT_PATH=n
94+IS_CHECK=n
95+IS_PRE_CHECK=n
96+IN_INSTALL_TYPE=""
97+IN_INSTALL_PATH=""
98+IS_DOCKER_INSTALL=n
99+IS_SETENV=n
100+IS_JIT=n
101+DOCKER_ROOT=""
102+CONFLICT_CMD_NUMS=0
103+IN_FEATURE="All"
104+ 
105+# ==================== Log Helper Functions ====================
106+startlog() {
107+ logandprint "[INFO]: Start Time: $(getdate)"
108+}
109+ 
110+exitlog() {
111+ logandprint "[INFO]: End Time: $(getdate)"
112+}
113+ 
114+# ==================== Version Functions ====================
115+get_package_version() {
116+ local var_name="$1"
117+ local version_file="$2"
118+ 
119+ if [ -f "${version_file}" ]; then
120+ local ver=$(grep "^${KEY_RUNPKG_VERSION}=" "${version_file}" 2>/dev/null | cut -d"=" -f2-)
121+ if [ -n "${ver}" ]; then
122+ eval "${var_name}=\"${ver}\""
123+ return
124+ fi
125+ fi
126+ eval "${var_name}=\"9.0.0\""
127+}
128+ 
129+get_installed_info() {
130+ local key="$1"
131+ local res=""
132+ if [ -f "${INSTALL_INFO_FILE}" ]; then
133+ chmod 644 "${INSTALL_INFO_FILE}" >/dev/null 2>&1
134+ res=$(cat "${INSTALL_INFO_FILE}" 2>/dev/null | grep "${key}" | awk -F = '{print $2}')
135+ fi
136+ echo "${res}"
137+}
138+ 
139+# ==================== Path Check Functions ====================
140+check_install_path_valid() {
141+ local path="$1"
142+ # 黑名单设置,不允许//,...这样的路径
143+ if echo "${path}" | grep -Eq '/{2,}|\.{3,}'; then
144+ return 1
145+ fi
146+ # 白名单设置,只允许常见字符
147+ if echo "${path}" | grep -Eq '^~?[a-zA-Z0-9./_-]*$'; then
148+ return 0
149+ else
150+ return 1
151+ fi
152+}
153+ 
154+judgment_path() {
155+ check_install_path_valid "${1}"
156+ if [ $? -ne 0 ]; then
157+ logandprint "[ERROR]: The install path ${1} is invalid, only characters in [a-z,A-Z,0-9,-,_,/,.] are supported!"
158+ exitlog
159+ exit 1
160+ fi
161+}
162+ 
163+check_install_path() {
164+ TARGET_INSTALL_PATH="$1"
165+ 
166+ # Empty path check
167+ if [ "x${TARGET_INSTALL_PATH}" = "x" ]; then
168+ logandprint "[ERROR]: ERR_NO:${PARAM_INVALID};ERR_DES:Parameter --install-path not support empty path."
169+ exitlog
170+ exit 1
171+ fi
172+ 
173+ # Space check
174+ if echo "x${TARGET_INSTALL_PATH}" | grep -q " "; then
175+ logandprint "[ERROR]: ERR_NO:${PARAM_INVALID};ERR_DES:Parameter --install-path cannot contain space character."
176+ exitlog
177+ exit 1
178+ fi
179+ 
180+ # Delete last "/"
181+ local temp_path="${TARGET_INSTALL_PATH}"
182+ temp_path=$(echo "${temp_path%/}")
183+ if [ x"${temp_path}" = "x" ]; then
184+ temp_path="/"
185+ fi
186+ 
187+ # Convert relative path to absolute path
188+ local prefix=$(echo "${temp_path}" | cut -d"/" -f1 | cut -d"~" -f1)
189+ if [ "x${prefix}" = "x" ]; then
190+ TARGET_INSTALL_PATH="${temp_path}"
191+ else
192+ prefix=$(echo "${RUN_PATH}" | cut -d"/" -f1 | cut -d"~" -f1)
193+ if [ x"${prefix}" = "x" ]; then
194+ TARGET_INSTALL_PATH="${RUN_PATH}/${temp_path}"
195+ else
196+ logandprint "[ERROR]: ERR_NO:${PARAM_INVALID};ERR_DES: Run package path is invalid: $RUN_PATH"
197+ exitlog
198+ exit 1
199+ fi
200+ fi
201+ 
202+ # Convert '~' to home path
203+ local home=$(echo "${TARGET_INSTALL_PATH}" | cut -d"~" -f1)
204+ if [ "x${home}" = "x" ]; then
205+ local temp_path_value=$(echo "${TARGET_INSTALL_PATH}" | cut -d"~" -f2)
206+ if [ "$(id -u)" -eq 0 ]; then
207+ TARGET_INSTALL_PATH="/root${temp_path_value}"
208+ else
209+ local home_path=$(eval echo "${HOME}")
210+ home_path=$(echo "${home_path}%/")
211+ TARGET_INSTALL_PATH="${home_path}${temp_path_value}"
212+ fi
213+ fi
214+}
215+ 
216+get_run_path() {
217+ RUN_PATH=$(echo "$2" | cut -d"-" -f3-)
218+ if [ x"${RUN_PATH}" = x"" ]; then
219+ RUN_PATH=$(pwd)
220+ else
221+ RUN_PATH=$(echo "${RUN_PATH%/}")
222+ if [ "x${RUN_PATH}" = "x" ]; then
223+ RUN_PATH=$(pwd)
224+ fi
225+ fi
226+}
227+ 
228+check_docker_path() {
229+ local docker_path="$1"
230+ if [ "${docker_path}" != "/"* ]; then
231+ logandprint "[ERROR]: ERR_NO:${PARAM_INVALID};ERR_DES:Parameter --docker-root must be absolute path starting with /."
232+ exitlog
233+ exit 1
234+ fi
235+ if [ ! -d "${docker_path}" ]; then
236+ logandprint "[ERROR]: ERR_NO:${FILE_NOT_EXIST}; The directory:${docker_path} not exist."
237+ exitlog
238+ exit 1
239+ fi
240+}
241+ 
242+# ==================== Version Directory Check ====================
243+is_version_dirpath() {
244+ local path="$1"
245+ local dirname=$(basename "$path")
246+ # Check if directory name looks like a version (e.g., cann, 9.0.0, etc.)
247+ if [[ "$dirname" =~ ^[0-9]+\.[0-9]+ ]] || [ "$dirname" = "cann" ]; then
248+ return 0
249+ fi
250+ return 1
251+}
252+ 
253+# ==================== Argument Parsing ====================
254+get_opts() {
255+ # Skip first two parameters (run package and directory)
256+ local i=0
257+ while true; do
258+ if [ "x$1" = "x" ]; then
259+ break
260+ fi
261+ if [ "$(expr substr "$1" 1 2)" = "--" ]; then
262+ i=$(expr $i + 1)
263+ fi
264+ if [ $i -gt 2 ]; then
265+ break
266+ fi
267+ shift 1
268+ done
269+ 
270+ if [ "$*" = "" ]; then
271+ logandprint "[ERROR]: ERR_NO:${PARAM_INVALID};ERR_DES:\
272+ only support one type: full/upgrade/uninstall, operation execute failed!\
273+ Please use [--help] to see the usage."
274+ exitlog
275+ exit 1
276+ fi
277+ 
278+ while true; do
279+ case "$1" in
280+ --full)
281+ IN_INSTALL_TYPE=$(echo "${1}" | awk -F"--" '{print $2}')
282+ IS_INSTALL="y"
283+ CONFLICT_CMD_NUMS=$(expr "$CONFLICT_CMD_NUMS" + 1)
284+ shift
285+ ;;
286+ --upgrade)
287+ IS_UPGRADE="y"
288+ CONFLICT_CMD_NUMS=$(expr "$CONFLICT_CMD_NUMS" + 1)
289+ shift
290+ ;;
291+ --uninstall)
292+ IS_UNINSTALL="y"
293+ CONFLICT_CMD_NUMS=$(expr "$CONFLICT_CMD_NUMS" + 1)
294+ shift
295+ ;;
296+ --install-path=*)
297+ IS_INPUT_PATH="y"
298+ IN_INSTALL_PATH=$(echo "${1}" | cut -d"=" -f2-)
299+ judgment_path "${IN_INSTALL_PATH}"
300+ check_install_path "${IN_INSTALL_PATH}"
301+ shift
302+ ;;
303+ --quiet)
304+ IS_QUIET="y"
305+ shift
306+ ;;
307+ --install-for-all)
308+ IS_FOR_ALL="y"
309+ shift
310+ ;;
311+ -*)
312+ logandprint "[ERROR]: ERR_NO:${PARAM_INVALID};ERR_DES:Unsupported parameter [$1],\
313+ operation execute failed. Please use [--help] to see the usage."
314+ exitlog
315+ exit 1
316+ ;;
317+ *)
318+ break
319+ ;;
320+ esac
321+ done
322+}
323+ 
324+check_opts() {
325+ if [ "${CONFLICT_CMD_NUMS}" != 1 ]; then
326+ logandprint "[ERROR]: ERR_NO:${PARAM_INVALID};ERR_DES:\
327+ only support one type: full/upgrade/uninstall, operation execute failed!\
328+ Please use [--help] to see the usage."
329+ exitlog
330+ exit 1
331+ fi
332+}
333+ 
334+# ==================== Environment Initialization ====================
335+init_env() {
336+ init_log_dir
337+ 
338+ if is_version_dirpath "$TARGET_INSTALL_PATH"; then
339+ PKG_VERSION_DIR="$(basename "$TARGET_INSTALL_PATH")"
340+ TARGET_INSTALL_PATH="$(dirname "$TARGET_INSTALL_PATH")"
341+ else
342+ PKG_VERSION_DIR="cann"
343+ fi
344+ TARGET_VERSION_DIR="$TARGET_INSTALL_PATH/$PKG_VERSION_DIR"
345+ 
346+ TARGET_SHARED_INFO_DIR="${TARGET_VERSION_DIR}/share/info"
347+ UNINSTALL_SHELL_FILE="${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}/script/opp_uninstall.sh"
348+ INSTALL_INFO_FILE="${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}/${ASCEND_INSTALL_INFO}"
349+ 
350+ logandprint "[INFO]: Execute the ops_fft run package."
351+ logandprint "[INFO]: OperationLogFile path: ${_INSTALL_LOG_FILE}."
352+ logandprint "[INFO]: Input params: $CMD_LIST"
353+ 
354+ get_package_version "RUN_PKG_VERSION" "$VERSION_INFO_FILE"
355+ local installed_version=$(get_installed_info "${KEY_INSTALLED_VERSION}")
356+ if [ "${installed_version}" = "" ]; then
357+ logandprint "[INFO]: Version of installing ops_fft module is ${RUN_PKG_VERSION}."
358+ else
359+ logandprint "[INFO]: Existed ops_fft module version is ${installed_version}, the new version is ${RUN_PKG_VERSION}."
360+ fi
361+}
362+ 
363+check_pre_install() {
364+ local installed_user=$(get_installed_info "${KEY_INSTALLED_UNAME}")
365+ local installed_group=$(get_installed_info "${KEY_INSTALLED_UGROUP}")
366+ 
367+ if [ "${installed_user}" != "" ] || [ "${installed_group}" != "" ]; then
368+ if [ "${installed_user}" != "${TARGET_USERNAME}" ] || [ "${installed_group}" != "${TARGET_USERGROUP}" ]; then
369+ logandprint "[ERROR]: The user and group are not same with last installation, do not support overwriting installation!"
370+ exitlog
371+ exit 1
372+ fi
373+ fi
374+ 
375+ if [ "${IS_UPGRADE}" = "y" ]; then
376+ if [ ! -e "${INSTALL_INFO_FILE}" ]; then
377+ logandprint "[ERROR]: ERR_NO:${FILE_NOT_EXIST}; The directory:${TARGET_INSTALL_PATH} has no ops_fft installed, upgrade failed."
378+ exitlog
379+ exit 1
380+ fi
381+ IN_INSTALL_TYPE=$(get_installed_info "${KEY_INSTALLED_TYPE}")
382+ fi
383+}
384+ 
385+mkdir_install_path() {
386+ local base_dir=$(dirname "${TARGET_INSTALL_PATH}")
387+ if [ ! -d "${base_dir}" ]; then
388+ logandprint "[ERROR]: ERR_NO:${FILE_NOT_EXIST}; The directory:${base_dir} not exist, please create this directory."
389+ exitlog
390+ exit 1
391+ fi
392+ 
393+ if [ -d "${TARGET_INSTALL_PATH}" ]; then
394+ test -w "${TARGET_INSTALL_PATH}" >>/dev/null 2>&1
395+ if [ "$?" -ne 0 ]; then
396+ logandprint "[ERROR]: ERR_NO:${PERM_DENIED};ERR_DES:${PERM_DENIED_DES}. User ${TARGET_USERNAME} cannot access ${TARGET_INSTALL_PATH}."
397+ exit 1
398+ fi
399+ else
400+ test -w "${base_dir}" >>/dev/null 2>&1
401+ if [ "$?" -ne 0 ]; then
402+ logandprint "[ERROR]: ERR_NO:${PERM_DENIED};ERR_DES:${PERM_DENIED_DES}. User ${TARGET_USERNAME} cannot access ${base_dir}."
403+ exit 1
404+ else
405+ mkdir -p "${TARGET_INSTALL_PATH}"
406+ if [ "$(id -u)" -eq 0 ] && [ "${IS_FOR_ALL}" = "y" ]; then
407+ chmod 755 "${TARGET_INSTALL_PATH}"
408+ else
409+ chmod 750 "${TARGET_INSTALL_PATH}"
410+ chown "${TARGET_USERNAME}:${TARGET_USERGROUP}" "${TARGET_INSTALL_PATH}" 2>/dev/null || true
411+ fi
412+ fi
413+ fi
414+}
415+ 
416+# ==================== Clean Before Reinstall ====================
417+clean_before_reinstall() {
418+ local installed_path=$(get_installed_info "${KEY_INSTALLED_PATH}")
419+ local existed_files=$(find "${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}" -type f -print 2>/dev/null)
420+ 
421+ if [ -z "${existed_files}" ]; then
422+ logandprint "[INFO]: Directory is empty, directly install ops_fft module."
423+ return 0
424+ fi
425+ 
426+ if [ "${IS_QUIET}" = "y" ]; then
427+ logandprint "[WARNING]: Directory has files existed or ops_fft installed, continuing installation."
428+ else
429+ if [ ! -f "${INSTALL_INFO_FILE}" ]; then
430+ logandprint "[INFO]: Directory has files existed, do you want to continue? [y/n]"
431+ else
432+ logandprint "[INFO]: ops_fft package has been installed at $(get_installed_info "${KEY_INSTALLED_PATH}"), version $(get_installed_info "${KEY_INSTALLED_VERSION}"), this package version is ${RUN_PKG_VERSION}, do you want to continue? [y/n]"
433+ fi
434+ while true; do
435+ read yn
436+ if [ "$yn" = "n" ]; then
437+ logandprint "[INFO]: Exit installation."
438+ exitlog
439+ exit 0
440+ elif [ "$yn" = "y" ]; then
441+ break
442+ else
443+ echo "[WARNING]: Input error, please input y or n!"
444+ fi
445+ done
446+ fi
447+ 
448+ if [ "${installed_path}" = "${TARGET_VERSION_DIR}" ]; then
449+ logandprint "[INFO]: Clean the installed ops_fft module before install."
450+ if [ -f "${UNINSTALL_SHELL_FILE}" ]; then
451+ bash "${UNINSTALL_SHELL_FILE}" "${TARGET_VERSION_DIR}" "upgrade" "${IS_QUIET}" "${IN_FEATURE}" "${IS_DOCKER_INSTALL}" "${DOCKER_ROOT}" "$PKG_VERSION_DIR"
452+ if [ "$?" != "0" ]; then
453+ logandprint "[ERROR]: Failed to clean installed directory."
454+ return 1
455+ fi
456+ fi
457+ fi
458+ return 0
459+}
460+ 
461+# ==================== Architecture Check ====================
462+check_arch() {
463+ local architecture=$(uname -m)
464+ if [ -n "${ARCH_INFO}" ] && [ "${architecture}" != "${ARCH_INFO}" ]; then
465+ logandprint "[WARNING]: The architecture of run package (${ARCH_INFO}) is different from current system (${architecture})."
466+ fi
467+}
468+ 
469+# ==================== Install Functions ====================
470+install_package() {
471+ if [ "${IS_INSTALL}" = "n" ] && [ "${IS_UPGRADE}" = "n" ]; then
472+ return
473+ fi
474+ 
475+ logandprint "[INFO]: ============================================"
476+ logandprint "[INFO]: Starting ops_fft installation..."
477+ logandprint "[INFO]: Version: ${RUN_PKG_VERSION}"
478+ logandprint "[INFO]: Install path: ${TARGET_VERSION_DIR}"
479+ logandprint "[INFO]: Install type: ${IN_INSTALL_TYPE}"
480+ logandprint "[INFO]: ============================================"
481+ 
482+ check_arch
483+ 
484+ # Clean before reinstall
485+ clean_before_reinstall
486+ if [ "$?" != "0" ]; then
487+ logandprint "[ERROR]: Installation failed during cleanup."
488+ exitlog
489+ exit 1
490+ fi
491+ 
492+ # Use opp_install.sh to install (通过 COMMON_PARSER_FILE 完成安装)
493+ if [ -f "${INSTALL_SHELL_FILE}" ]; then
494+ bash "${INSTALL_SHELL_FILE}" "${TARGET_INSTALL_PATH}" "${TARGET_USERNAME}" "${TARGET_USERGROUP}" \
495+ "${IN_FEATURE}" "${IN_INSTALL_TYPE}" "${IS_FOR_ALL}" "${IS_SETENV}" \
496+ "${IS_DOCKER_INSTALL}" "${DOCKER_ROOT}" "${PKG_VERSION_DIR}"
497+ if [ "$?" != "0" ]; then
498+ logandprint "[ERROR]: Installation failed."
499+ exitlog
500+ exit 1
501+ fi
502+ else
503+ logandprint "[ERROR]: opp_install.sh not found at ${INSTALL_SHELL_FILE}"
504+ exitlog
505+ exit 1
506+ fi
507+ 
508+ logandprint "[INFO]: ============================================"
509+ logandprint "[INFO]: Installation completed successfully!"
510+ logandprint "[INFO]: Install path: ${TARGET_VERSION_DIR}/${OPP_PLATFORM_DIR}"
511+ logandprint "[INFO]:"
512+ logandprint "[INFO]: To use ops-fft, you may need to set environment:"
513+ logandprint "[INFO]: source ${TARGET_VERSION_DIR}/set_env.bash"
514+ logandprint "[INFO]: ============================================"
515+}
516+ 
517+# ==================== Uninstall Functions ====================
518+uninstall_package() {
519+ if [ "${IS_UNINSTALL}" = "n" ]; then
520+ return
521+ fi
522+ 
523+ logandprint "[INFO]: Starting uninstallation..."
524+ 
525+ # Check if ops_fft is installed by looking for install info file
526+ if [ ! -f "${INSTALL_INFO_FILE}" ]; then
527+ logandprint "[INFO]: ops_fft is not installed (no install info found at ${INSTALL_INFO_FILE})"
528+ exitlog
529+ exit 0
530+ fi
531+ 
532+ # Use opp_uninstall.sh to uninstall (通过 COMMON_PARSER_FILE 完成卸载)
533+ if [ -f "${UNINSTALL_SHELL_FILE}" ]; then
534+ bash "${UNINSTALL_SHELL_FILE}" "${TARGET_INSTALL_PATH}" "uninstall" "${IS_QUIET}" "${IN_FEATURE}" \
535+ "${IS_DOCKER_INSTALL}" "${DOCKER_ROOT}" "${PKG_VERSION_DIR}"
536+ if [ "$?" != "0" ]; then
537+ logandprint "[ERROR]: Uninstallation failed."
538+ exitlog
539+ exit 1
540+ fi
541+ else
542+ logandprint "[ERROR]: opp_uninstall.sh not found at ${UNINSTALL_SHELL_FILE}"
543+ exitlog
544+ exit 1
545+ fi
546+ 
547+ logandprint "[INFO]: Uninstallation completed successfully."
548+}
549+ 
550+# ==================== Main Function ====================
551+main() {
552+ get_run_path "$@"
553+ startlog
554+ get_opts "$@"
555+ check_opts
556+ init_env
557+ check_pre_install
558+ mkdir_install_path
559+ install_package
560+ uninstall_package
561+ exitlog
562+}
563+ 
564+main "$@"
565+exit 0
Ascripts/package/ops_fft/scripts/opp_common.sh+45-0
@@ -0,0 +1,45 @@
1+#!/bin/bash
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+# ops-fft Common Functions Library
12+# ----------------------------------------------------------------------------
13+ 
14+# ==================== Log Directory Initialization ====================
15+init_log_dir() {
16+ if [ "$(id -u)" != "0" ]; then
17+ _LOG_PATH="${HOME}/var/log/ascend_seclog"
18+ else
19+ _LOG_PATH="/var/log/ascend_seclog"
20+ fi
21+ _INSTALL_LOG_FILE="${_LOG_PATH}/ascend_install.log"
22+ 
23+ if [ ! -d "${_LOG_PATH}" ]; then
24+ mkdir -p "${_LOG_PATH}" 2>/dev/null || true
25+ fi
26+}
27+ 
28+# ==================== Log Functions ====================
29+getdate() {
30+ local _cur_date=$(date +"%Y-%m-%d %H:%M:%S")
31+ echo "${_cur_date}"
32+}
33+ 
34+logandprint() {
35+ local is_error_level=$(echo "$1" | grep -E 'ERROR|WARN|INFO')
36+ if [ "${is_quiet}" != "y" ] || [ "${is_error_level}" != "" ]; then
37+ echo "[OpsTensor] [$(getdate)] ""$1"
38+ fi
39+ if [ -d "${_LOG_PATH}" ]; then
40+ echo "[OpsTensor] [$(getdate)] ""$1" >>"${_INSTALL_LOG_FILE}"
41+ fi
42+}
43+ 
44+# Initialize log directory when sourced
45+init_log_dir
Ascripts/package/ops_fft/scripts/opp_install.sh+260-0
@@ -0,0 +1,260 @@
1+#!/bin/bash
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+PARAM_INVALID="0x0002"
13+INSTALL_FAILED="0x0000"
14+INSTALL_FAILED_DES="Update successfully."
15+FILE_NOT_EXIST="0x0080"
16+FILE_NOT_EXIST_DES="File not found."
17+FILE_READ_FAILED="0x0082"
18+FILE_READ_FAILED_DES="File read failed."
19+FILE_WRITE_FAILED="0x0081"
20+FILE_WRITE_FAILED_DES="File write failed."
21+PERM_DENIED="0x0093"
22+PERM_DENIED_DES="Permission denied."
23+ 
24+# run package's files info
25+SOURCEDIR="$PWD/ops_fft"
26+CURR_PATH=$(dirname "$(readlink -f "$0")")
27+VERSION_INFO_FILE="${CURR_PATH}/../version.info"
28+FILELIST_FILE="${CURR_PATH}/filelist.csv"
29+COMMON_PARSER_FILE="${CURR_PATH}/install_common_parser.sh"
30+SCENE_FILE="${CURR_PATH}/../scene.info"
31+ASCEND_INSTALL_INFO="ascend_install.info"
32+ 
33+ARCH_INFO=$(uname -m)
34+OPP_PLATFORM_DIR=ops_fft
35+OPP_PLATFORM_UPPER=$(echo "${OPP_PLATFORM_DIR}" | tr '[:lower:]' '[:upper:]')
36+ 
37+TARGET_INSTALL_PATH=""
38+TARGET_MOULDE_DIR="" # TARGET_INSTALL_PATH + PKG_VERSION_DIR + OPP_PLATFORM_DIR
39+TARGET_VERSION_DIR="" # TARGET_INSTALL_PATH + PKG_VERSION_DIR
40+TARGET_SHARED_INFO_DIR="" # TARGET_INSTALL_PATH + PKG_VERSION_DIR + share/info
41+TARGET_OPP_BUILT_IN=""
42+ 
43+COMMON_INC_FILE="${CURR_PATH}/common_func.inc"
44+COMMON_FUNC_V2_PATH="${CURR_PATH}/common_func_v2.inc"
45+VERSION_CFG="${CURR_PATH}/version_cfg.inc"
46+OPP_COMMON_FILE="${CURR_PATH}/opp_common.sh"
47+ 
48+. "${COMMON_INC_FILE}"
49+. "${COMMON_FUNC_V2_PATH}"
50+. "${VERSION_CFG}"
51+. "${OPP_COMMON_FILE}"
52+ 
53+# keys of infos in ascend_install.info
54+KEY_INSTALLED_UNAME="USERNAME"
55+KEY_INSTALLED_UGROUP="USERGROUP"
56+KEY_INSTALLED_TYPE="${OPP_PLATFORM_UPPER}_INSTALL_TYPE"
57+KEY_INSTALLED_FEATURE="${OPP_PLATFORM_UPPER}_INSTALL_FEATURE"
58+KEY_INSTALLED_CHIP="${OPP_PLATFORM_UPPER}_INSTALL_CHIP"
59+KEY_INSTALLED_PATH="${OPP_PLATFORM_UPPER}_INSTALL_PATH_VAL"
60+KEY_INSTALLED_VERSION="${OPP_PLATFORM_UPPER}_VERSION"
61+ 
62+get_opts() {
63+ TARGET_INSTALL_PATH="$1"
64+ TARGET_USERNAME="$2"
65+ TARGET_USERGROUP="$3"
66+ IN_FEATURE="$4"
67+ INSTALL_TYPE="$5"
68+ IS_FOR_ALL="$6"
69+ IS_SETENV="$7"
70+ IS_DOCKER_INSTALL="$8"
71+ DOCKER_ROOT="$9"
72+ PKG_VERSION_DIR="${10}"
73+ 
74+ if [ "${TARGET_INSTALL_PATH}" = "" ] || [ "${TARGET_USERNAME}" = "" ] ||
75+ [ "${TARGET_USERGROUP}" = "" ] || [ "${INSTALL_TYPE}" = "" ]; then
76+ logandprint "[ERROR]: ERR_NO:${PARAM_INVALID};ERR_DES:Empty parameters is invalid for install."
77+ exit 1
78+ fi
79+ 
80+ INSTALL_FOR_ALL=""
81+ if [ "${IS_FOR_ALL}" = "y" ]; then
82+ INSTALL_FOR_ALL="--install_for_all"
83+ fi
84+}
85+ 
86+init_install_env() {
87+ get_package_version "RUN_PKG_VERSION" "$VERSION_INFO_FILE"
88+ if [ "${PKG_VERSION_DIR}" = "" ]; then
89+ TARGET_VERSION_DIR=${TARGET_INSTALL_PATH}
90+ else
91+ TARGET_VERSION_DIR=${TARGET_INSTALL_PATH}/${PKG_VERSION_DIR}
92+ fi
93+ TARGET_MOULDE_DIR=${TARGET_VERSION_DIR}/${OPP_PLATFORM_DIR}
94+ TARGET_OPP_BUILT_IN=${TARGET_VERSION_DIR}/opp/built-in
95+ TARGET_SHARED_INFO_DIR=${TARGET_VERSION_DIR}/share/info
96+ INSTALL_INFO_FILE=${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}/${ASCEND_INSTALL_INFO}
97+ 
98+ if [ "$(id -u)" != "0" ]; then
99+ LOG_PATH_PERM="740"
100+ LOG_FILE_PERM="640"
101+ INSTALL_INFO_PERM="600"
102+ else
103+ LOG_PATH_PERM="750"
104+ LOG_FILE_PERM="640"
105+ INSTALL_INFO_PERM="644"
106+ fi
107+ 
108+ if [ "${IS_FOR_ALL}" = "y" ]; then
109+ BUILTIN_PERM="555"
110+ CUSTOM_PERM="755"
111+ CREATE_DIR_PERM="755"
112+ ONLYREAD_PERM="444"
113+ else
114+ BUILTIN_PERM="550"
115+ CUSTOM_PERM="750"
116+ CREATE_DIR_PERM="750"
117+ ONLYREAD_PERM="440"
118+ fi
119+}
120+ 
121+log_with_errorlevel() {
122+ local ret_status="$1"
123+ local level="$2"
124+ local msg="$3"
125+ if [ "${ret_status}" != 0 ]; then
126+ if [ "${level}" = "error" ]; then
127+ logandprint "${msg}"
128+ exit 1
129+ else
130+ logandprint "${msg}"
131+ fi
132+ fi
133+}
134+ 
135+get_installed_info() {
136+ local key="$1"
137+ local res=""
138+ if [ -f "${INSTALL_INFO_FILE}" ]; then
139+ res=$(cat "${INSTALL_INFO_FILE}" | grep "${key}" | awk -F = '{print $2}')
140+ fi
141+ echo "${res}"
142+}
143+ 
144+update_install_info() {
145+ local key_val="$1"
146+ local val="$2"
147+ local old_val=$(get_installed_info "${key_val}")
148+ if [ -f "${INSTALL_INFO_FILE}" ]; then
149+ if [ "x${old_val}" = "x" ]; then
150+ echo "${key_val}=${val}" >>"${INSTALL_INFO_FILE}"
151+ else
152+ sed -i "/${key_val}/c ${key_val}=${val}" "${INSTALL_INFO_FILE}"
153+ fi
154+ else
155+ echo "${key_val}=${val}" >"${INSTALL_INFO_FILE}"
156+ fi
157+}
158+ 
159+update_install_infos() {
160+ local uname="$1"
161+ local ugroup="$2"
162+ local type="$3"
163+ local path="$4"
164+ local version
165+ get_package_version "version" "$VERSION_INFO_FILE"
166+ comm_create_file "${INSTALL_INFO_FILE}" "${INSTALL_INFO_PERM}" "${TARGET_USERNAME}:${TARGET_USERGROUP}" "${IS_FOR_ALL}"
167+ 
168+ update_install_info "${KEY_INSTALLED_UNAME}" "${uname}"
169+ update_install_info "${KEY_INSTALLED_UGROUP}" "${ugroup}"
170+ update_install_info "${KEY_INSTALLED_TYPE}" "${type}"
171+ update_install_info "${KEY_INSTALLED_PATH}" "${path}"
172+ update_install_info "${KEY_INSTALLED_VERSION}" "${version}"
173+}
174+ 
175+check_file_exist() {
176+ local path_param="${1}"
177+ if [ ! -f "${path_param}" ]; then
178+ logandprint "[ERROR]: ERR_NO:${FILE_NOT_EXIST};ERR_DES:The file (${path_param}) does not existed."
179+ exit 1
180+ fi
181+}
182+ 
183+check_env() {
184+ check_file_exist "${FILELIST_FILE}"
185+ check_file_exist "${COMMON_PARSER_FILE}"
186+}
187+ 
188+get_install_path() {
189+ local docker_root_tmp="$(echo "${DOCKER_ROOT}" | sed 's#/\+$##g')"
190+ local docker_root_regex="$(echo "${docker_root_tmp}" | sed 's#/#\\/#g')"
191+ relative_path_val=$(echo "${TARGET_VERSION_DIR}" | sed "s/^${docker_root_regex}//g" | sed 's#/\+$##g')
192+ return
193+}
194+ 
195+setenv() {
196+ if [ "${IS_DOCKER_INSTALL}" = y ]; then
197+ INSTALL_OPTION="--docker-root=${DOCKER_ROOT}"
198+ else
199+ INSTALL_OPTION=""
200+ fi
201+ if [ "${IS_SETENV}" = "y" ]; then
202+ INSTALL_OPTION="${INSTALL_OPTION} --setenv"
203+ fi
204+}
205+ 
206+install_opp() {
207+ logandprint "[INFO]: Begin install opp module."
208+ comm_create_dir "${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}" "${CREATE_DIR_PERM}" "${TARGET_USERNAME}:${TARGET_USERGROUP}" "${IS_FOR_ALL}"
209+ 
210+ setenv
211+ 
212+ logandprint "[INFO]: Update the opp install info."
213+ 
214+ update_install_infos "${TARGET_USERNAME}" "${TARGET_USERGROUP}" "${INSTALL_TYPE}" "${relative_path_val}"
215+ log_with_errorlevel "$?" "error" "[ERROR]: ERR_NO:${INSTALL_FAILED};ERR_DES:Update opp install info failed."
216+ 
217+ bash "${COMMON_PARSER_FILE}" --package="${OPP_PLATFORM_DIR}" --install --username="${TARGET_USERNAME}" \
218+ --usergroup="${TARGET_USERGROUP}" --set-cann-uninstall --version=$RUN_PKG_VERSION \
219+ --use-share-info --version-dir=$PKG_VERSION_DIR $INSTALL_OPTION ${INSTALL_FOR_ALL} "--feature=all" "--chip=all" \
220+ "${INSTALL_TYPE}" "${TARGET_INSTALL_PATH}" "${FILELIST_FILE}"
221+ log_with_errorlevel "$?" "error" "[ERROR]: ERR_NO:${INSTALL_FAILED};ERR_DES:Install opp module files failed."
222+ 
223+}
224+ 
225+main() {
226+ logandprint "[INFO]: Command opp_install"
227+ 
228+ get_opts "$@"
229+ 
230+ init_install_env
231+ 
232+ get_package_upgrade_version_dir "upgrade_version_dir" "$TARGET_INSTALL_PATH" "${OPP_PLATFORM_DIR}"
233+ get_package_last_installed_version "last_installed" "$TARGET_INSTALL_PATH" "${OPP_PLATFORM_DIR}"
234+ last_installed_version=$(echo "${last_installed}" | cut --only-delimited -d":" -f2-)
235+ 
236+ get_install_path
237+ 
238+ check_env
239+ 
240+ install_opp
241+ 
242+ # change log dir and file owner and rights
243+ chmod "${LOG_PATH_PERM}" "${COMM_LOG_DIR}" 2>/dev/null
244+ chmod "${LOG_FILE_PERM}" "${COMM_LOGFILE}" 2>/dev/null
245+ chmod "${LOG_FILE_PERM}" "${COMM_OPERATION_LOGFILE}" 2>/dev/null
246+ 
247+ chmod "${ONLYREAD_PERM}" "${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}/scene.info" 2>/dev/null
248+ chmod "${ONLYREAD_PERM}" "${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}/version.info" 2>/dev/null
249+ chmod "${ONLYREAD_PERM}" "${INSTALL_INFO_FILE}" 2>/dev/null
250+ 
251+ logandprint "[INFO]: Installation information listed below:"
252+ logandprint "[INFO]: Install path: (${TARGET_VERSION_DIR}/opp)"
253+ logandprint "[INFO]: Install log file path: (${COMM_LOGFILE})"
254+ logandprint "[INFO]: Operation log file path: (${COMM_OPERATION_LOGFILE})"
255+ 
256+ logandprint "[INFO]: Opp package installed successfully! The new version takes effect immediately."
257+}
258+ 
259+main "$@"
260+exit 0
Ascripts/package/ops_fft/scripts/opp_uninstall.sh+184-0
@@ -0,0 +1,184 @@
1+#!/bin/bash
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+OPERATE_FAILED="0x0001"
13+PARAM_INVALID="0x0002"
14+FILE_NOT_EXIST="0x0080"
15+ 
16+CURR_PATH=$(dirname "$(readlink -f "$0")")
17+COMMON_INC_FILE="${CURR_PATH}/common_func.inc"
18+OPP_COMMON_FILE="${CURR_PATH}/opp_common.sh"
19+ 
20+. "${COMMON_INC_FILE}"
21+. "${OPP_COMMON_FILE}"
22+ 
23+ARCH_INFO=$(uname -m)
24+OPP_PLATFORM_DIR=ops_fft
25+OPP_PLATFORM_UPPER=$(echo "${OPP_PLATFORM_DIR}" | tr '[:lower:]' '[:upper:]')
26+FILELIST_FILE="${CURR_PATH}/filelist.csv"
27+COMMON_PARSER_FILE="${CURR_PATH}/install_common_parser.sh"
28+ 
29+TARGET_INSTALL_PATH=""
30+TARGET_VERSION_DIR="${CURR_PATH}/../../../.."
31+TARGET_VERSION_DIR=$(readlink -f "${TARGET_VERSION_DIR}")
32+TARGET_SHARED_INFO_DIR="${TARGET_VERSION_DIR}/share/info"
33+ 
34+ASCEND_INSTALL_INFO="ascend_install.info"
35+INSTALL_INFO_FILE="${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}/${ASCEND_INSTALL_INFO}"
36+VERSION_INFO_FILE="${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}/version.info"
37+ 
38+# keys of infos in ascend_install.info
39+KEY_INSTALLED_UNAME="USERNAME"
40+KEY_INSTALLED_UGROUP="USERGROUP"
41+KEY_INSTALLED_TYPE="${OPP_PLATFORM_UPPER}_INSTALL_TYPE"
42+KEY_INSTALLED_PATH="${OPP_PLATFORM_UPPER}_INSTALL_PATH_VAL"
43+ 
44+get_opts() {
45+ INSTALLED_PATH="$1"
46+ UNINSTALL_MODE="$2"
47+ IS_QUIET="$3"
48+ IN_FEATURE="$4"
49+ IS_DOCKER_INSTALL="$5"
50+ DOCKER_ROOT="$6"
51+ PKG_VERSION_DIR="$7"
52+ local paramter_num="$#"
53+ 
54+ if [ "${paramter_num}" != 0 ]; then
55+ if [ "${INSTALLED_PATH}" = "" ] ||
56+ [ "${UNINSTALL_MODE}" = "" ] ||
57+ [ "${IS_QUIET}" = "" ]; then
58+ logandprint "[ERROR]: ERR_NO:${PARAM_INVALID};ERR_DES:Empty paramters is invalid for uninstall."
59+ exit 1
60+ fi
61+ fi
62+}
63+ 
64+get_docker_install_path() {
65+ local docker_root_tmp="$(echo "${DOCKER_ROOT}" | sed 's#/\+$##g')"
66+ local docker_root_regex="$(echo "${docker_root_tmp}" | sed 's#/#\\/#g')"
67+ relative_path_val=$(echo "${TARGET_VERSION_DIR}" | sed "s/^${docker_root_regex}//g" | sed 's#/\+$##g')
68+}
69+ 
70+log_with_errorlevel() {
71+ local ret_status="$1"
72+ local level="$2"
73+ local msg="$3"
74+ if [ "${ret_status}" != 0 ]; then
75+ if [ "${level}" = "error" ]; then
76+ logandprint "${msg}"
77+ exit 1
78+ else
79+ logandprint "${msg}"
80+ fi
81+ fi
82+}
83+ 
84+check_file_exist() {
85+ local path_param="${1}"
86+ if [ ! -f "${path_param}" ]; then
87+ logandprint "[ERROR]: ERR_NO:${FILE_NOT_EXIST};ERR_DES:The file (${path_param}) does not existed."
88+ exit 1
89+ fi
90+}
91+ 
92+check_installed_files() {
93+ check_file_exist "${INSTALL_INFO_FILE}"
94+ check_file_exist "${FILELIST_FILE}"
95+ check_file_exist "${COMMON_PARSER_FILE}"
96+}
97+ 
98+check_installed_type() {
99+ local type="$1"
100+ if [ "${type}" != "run" ] && [ "${type}" != "full" ] && [ "${type}" != "devel" ]; then
101+ logandprint "[ERROR]: Install type of opp module is not right!"
102+ exit 1
103+ fi
104+}
105+ 
106+unsetenv() {
107+ if [ "${IS_DOCKER_INSTALL}" = y ]; then
108+ UNINSTALL_OPTION="--docker-root=${DOCKER_ROOT}"
109+ else
110+ UNINSTALL_OPTION=""
111+ fi
112+}
113+ 
114+get_installed_info() {
115+ local key="$1"
116+ local res=""
117+ if [ -f "${INSTALL_INFO_FILE}" ]; then
118+ chmod 644 "${INSTALL_INFO_FILE}" >/dev/null 2>&1
119+ res=$(cat "${INSTALL_INFO_FILE}" | grep "${key}" | awk -F = '{print $2}')
120+ fi
121+ echo "${res}"
122+}
123+ 
124+get_installed_param() {
125+ INSTALLED_TYPE=$(get_installed_info "${KEY_INSTALLED_TYPE}")
126+ TARGET_USERNAME=$(get_installed_info "${KEY_INSTALLED_UNAME}")
127+ TARGET_USERGROUP=$(get_installed_info "${KEY_INSTALLED_UGROUP}")
128+ get_package_version "RUN_PKG_VERSION" "$VERSION_INFO_FILE"
129+ if [ "${PKG_VERSION_DIR}" = "" ]; then
130+ TARGET_INSTALL_PATH=${TARGET_VERSION_DIR}
131+ else
132+ TARGET_INSTALL_PATH=$(readlink -f "${TARGET_VERSION_DIR}/../")
133+ fi
134+}
135+ 
136+remove_module() {
137+ chmod u+w "${TARGET_SHARED_INFO_DIR}/${OPP_PLATFORM_DIR}/scene.info" 2>/dev/null
138+ 
139+ logandprint "[INFO]: Delete the installed opp source files in (${TARGET_VERSION_DIR})."
140+ 
141+ bash "${COMMON_PARSER_FILE}" --package="${OPP_PLATFORM_DIR}" --uninstall --remove-install-info \
142+ --username="${TARGET_USERNAME}" --usergroup="${TARGET_USERGROUP}" --version=$RUN_PKG_VERSION \
143+ --use-share-info --version-dir=$PKG_VERSION_DIR ${UNINSTALL_OPTION} "${INSTALLED_TYPE}" "${TARGET_INSTALL_PATH}" \
144+ "${FILELIST_FILE}" "${IN_FEATURE}" --recreate-softlink
145+ log_with_errorlevel "$?" "error" "[ERROR]: ERR_NO:${OPERATE_FAILED};ERR_DES:Uninstall opp module failed."
146+}
147+ 
148+remove_ops_fft() {
149+ remove_module
150+ 
151+ if [ "${UNINSTALL_MODE}" != "upgrade" ]; then
152+ logandprint "[INFO]: Delete the install info file (${INSTALL_INFO_FILE})."
153+ rm -f "${INSTALL_INFO_FILE}"
154+ log_with_errorlevel "$?" "warn" "[WARNING] Delete ops install info file failed, please delete it by yourself."
155+ fi
156+}
157+ 
158+logandprint "[INFO]: Begin uninstall the opp module."
159+ 
160+main() {
161+ get_opts "$@"
162+ 
163+ get_docker_install_path
164+ 
165+ check_installed_files
166+ 
167+ get_installed_param
168+ 
169+ check_installed_type "${INSTALLED_TYPE}"
170+ 
171+ unsetenv
172+ 
173+ remove_ops_fft
174+ 
175+ if [ "${UNINSTALL_MODE}" != "upgrade" ]; then
176+ remove_dir_if_empty "${TARGET_VERSION_DIR}"
177+ fi
178+ remove_dir_if_empty "${INSTALLED_PATH}"
179+ 
180+ logandprint "[INFO]: Opp package uninstalled successfully! Uninstallation takes effect immediately."
181+}
182+ 
183+main "$@"
184+exit 0
Ascripts/package/ops_fft/scripts/uninstall.sh+71-0
@@ -0,0 +1,71 @@
1+#!/bin/bash
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+# ops-fft Uninstall Script
12+# ----------------------------------------------------------------------------
13+ 
14+CURR_PATH=$(dirname "$(readlink -f "$0")")
15+OPP_PLATFORM_DIR="ops_fft"
16+ 
17+# Source common functions
18+OPP_COMMON_FILE="${CURR_PATH}/opp_common.sh"
19+if [ -f "${OPP_COMMON_FILE}" ]; then
20+ . "${OPP_COMMON_FILE}"
21+fi
22+ 
23+# ==================== Argument Parsing ====================
24+IS_QUIET=n
25+QUIET_PARAMETER=""
26+RET_STATUS=""
27+ 
28+if [ "$#" != "0" ]; then
29+ if [ "$1" = "--quiet" ] && [ "$#" = "1" ]; then
30+ IS_QUIET=y
31+ QUIET_PARAMETER="--quiet"
32+ else
33+ logandprint "Please use correct parameters, only support input nothing or only --quiet parameter."
34+ exit 1
35+ fi
36+fi
37+ 
38+# Set install path from script location
39+INSTALLED_PATH="$(cd "${CURR_PATH}/../../../.."; pwd)"
40+ 
41+logandprint "[INFO]: Starting ops_fft uninstallation..."
42+logandprint "[INFO]: Target path: ${INSTALLED_PATH}"
43+ 
44+# Check for install script
45+INSTALL_SHELL="${CURR_PATH}/install.sh"
46+if [ -f "${INSTALL_SHELL}" ]; then
47+ logandprint "[INFO]: Using install script for uninstallation..."
48+ PARENT_INSTALLED_PATH="$(cd "${INSTALLED_PATH}/.."; pwd)"
49+ 
50+ sh "${INSTALL_SHELL}" "--aa" "--aa" "--uninstall" "--install-path=${INSTALLED_PATH}" "${QUIET_PARAMETER}"
51+ RET_STATUS="$?"
52+ 
53+ if [ "${RET_STATUS}" != "0" ]; then
54+ logandprint "[ERROR]: Uninstallation failed with status ${RET_STATUS}"
55+ exit 1
56+ fi
57+ 
58+ # Clean empty parent directory
59+ if [ -d "${PARENT_INSTALLED_PATH}" ]; then
60+ SUBDIRS_PARAM_INSTALL=$(ls "${PARENT_INSTALLED_PATH}" 2>/dev/null)
61+ if [ "${SUBDIRS_PARAM_INSTALL}" = "" ]; then
62+ rm -rf "${PARENT_INSTALLED_PATH}"
63+ fi
64+ fi
65+else
66+ logandprint "[ERROR]: install.sh not found at ${INSTALL_SHELL}"
67+ exit 1
68+fi
69+ 
70+logandprint "[INFO]: Uninstallation completed successfully."
71+exit 0
Ascripts/package/ops_fft/scripts/ver_check.sh+177-0
@@ -0,0 +1,177 @@
1+#!/bin/bash
2+# ----------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+REQ_VER_PATH="$1"
13+_CURR_PATH=$(dirname "$(readlink -f "$0")")
14+_DEFAULT_INSTALL_PATH="/usr/local/Ascend"
15+FILE_NOT_EXIST="0x0080"
16+ 
17+# Source common functions
18+OPP_COMMON_FILE="${_CURR_PATH}/opp_common.sh"
19+if [ -f "${OPP_COMMON_FILE}" ]; then
20+ . "${OPP_COMMON_FILE}"
21+fi
22+ 
23+check_path_pre() {
24+ local in_checkpath_0="$1"
25+ local in_checkpath_1=$(echo "${in_checkpath_0}" | cut -d"=" -f2)
26+ if [ "${in_checkpath_1}" = "" ]; then
27+ logandprint "[WARNING]: please input correct path"
28+ exit 1
29+ fi
30+ local arr=$(echo "${in_checkpath_1}" | awk '{split($0,arr," ");for(i in arr) print arr[i]}')
31+ local index=0
32+ local id
33+ for i in ${arr}; do
34+ id=$((${id:=-1} + 1))
35+ eval "arr_${id}=\$i"
36+ index=$(expr "$index" + 1)
37+ done
38+ local len="${index}"
39+ local b=0
40+ local ret
41+ for i in $(seq 0 "${len}"); do
42+ select_last_dir_component "$(eval echo "\$arr_$i")"
43+ ret="$last_component"
44+ if [ "${ret}" != "" ]; then
45+ eval "checked_path_temp_${b}=\$(eval echo \"\$arr_$i\")"
46+ b=$(expr "$b" + 1)
47+ fi
48+ done
49+ local check_all_path=""
50+ for i in $(seq 0 "${len}"); do
51+ check_all_path="$(eval echo "\$checked_path_temp_$i") ${check_all_path}"
52+ done
53+ checked_path="${check_all_path}"
54+ return
55+}
56+ 
57+select_last_dir_component() {
58+ local path="$1"
59+ last_component=$(basename "${path}")
60+ if [ "${last_component}" = "atc" ]; then
61+ last_component="atc"
62+ return
63+ elif [ "${last_component}" = "fwkacllib" ]; then
64+ last_component="fwkacllib"
65+ return
66+ elif [ "${last_component}" = "compiler" ]; then
67+ last_component="compiler"
68+ return
69+ elif [ "${last_component}" = "fwkplugin" ]; then
70+ last_component="fwkplugin"
71+ return
72+ else
73+ last_component="atc or fwkacllib or compiler"
74+ return
75+ fi
76+}
77+ 
78+check_version_file() {
79+ local pkg_path="$1"
80+ local component_ret="$2"
81+ local run_pkg_path_temp=$(dirname "${pkg_path}")
82+ local run_pkg_path="${run_pkg_path_temp}/${component_ret}"
83+ version_file="${run_pkg_path}/version.info"
84+ if [ -f "${version_file}" ]; then
85+ echo "${version_file}" 2 >>/dev/null
86+ else
87+ logandprint "[ERROR]: ERR_NO:${FILE_NOT_EXIST}; The [${component_ret}] version.info in path [${pkg_path}] not exists."
88+ exit 1
89+ fi
90+ return
91+}
92+ 
93+check_opp_version_file() {
94+ if [ -f "${_CURR_PATH}/../../version.info" ]; then
95+ ver_info="${_CURR_PATH}/../../version.info"
96+ # ops_fft/version.info -> ops_fft
97+ elif [ -f "${_DEFAULT_INSTALL_PATH}/ops_fft/version.info" ]; then
98+ ver_info="${_DEFAULT_INSTALL_PATH}/ops_fft/version.info"
99+ else
100+ logandprint "[ERROR]: ERR_NO:${FILE_NOT_EXIST}; The [ops_fft] version.info not exists."
101+ fi
102+ return
103+}
104+ 
105+check_relation() {
106+ local opp_ver_info="$1"
107+ local req_pkg_name="$2"
108+ local req_pkg_version="$3"
109+ local _COMMON_INC_FILE="${_CURR_PATH}/common_func.inc"
110+ if [ -f "${_COMMON_INC_FILE}" ]; then
111+ . "${_COMMON_INC_FILE}"
112+ check_pkg_ver_deps "${opp_ver_info}" "${req_pkg_name}" "${req_pkg_version}"
113+ ret_situation="$ver_check_status"
114+ else
115+ logandprint "[ERROR]: ERR_NO:${FILE_NOT_EXIST}; The ${_COMMON_INC_FILE} not exists."
116+ fi
117+ return
118+}
119+ 
120+show_relation() {
121+ local relation_situation="$1"
122+ local req_pkg_name_val="$2"
123+ local req_pkg_path="$3"
124+ if [ "$relation_situation" = "SUCC" ]; then
125+ logandprint "[INFO]: Relationship of ops_fft with ${req_pkg_name_val} in path ${req_pkg_path} check successfully"
126+ return 0
127+ else
128+ logandprint "[WARNING]: Relationship of ops_fft with ${req_pkg_name_val} in path ${req_pkg_path} check failed. \
129+do you want to continue. [y/n] "
130+ local yn
131+ while true; do
132+ read yn
133+ if [ "$yn" = "n" ]; then
134+ echo "stop check!"
135+ exit 1
136+ elif [ "$yn" = "y" ]; then
137+ break
138+ else
139+ echo "[WARNING]: Input error, please input y or n to choose!"
140+ fi
141+ done
142+ fi
143+}
144+ 
145+version_check() {
146+ local path_val="$1"
147+ #get ops_fft version
148+ check_opp_version_file
149+ local ret_check_opp_version_file="$ver_info"
150+ #get checked path
151+ check_path_pre "${path_val}"
152+ local ret_check_path_pre="$checked_path"
153+ if [ "${ret_check_path_pre}" != "" ]; then
154+ local var
155+ local ret_last_component
156+ local ret_check_version_file
157+ local ret_check_relation
158+ for var in ${ret_check_path_pre}; do
159+ # select_last_dir_component "${var}"
160+ # component_ret=$last_component
161+ #get atc or fwkacllib name
162+ select_last_dir_component "${var}"
163+ ret_last_component="$last_component"
164+ #get the version of atc/fwkacllib
165+ check_version_file "${var}" "${ret_last_component}"
166+ ret_check_version_file="$version_file"
167+ #check relation
168+ check_relation "${ret_check_opp_version_file}" "${ret_last_component}" "${ret_check_version_file}"
169+ ret_check_relation="$ret_situation"
170+ #show relation
171+ show_relation "${ret_check_relation}" "${ret_last_component}" "${var}"
172+ done
173+ fi
174+}
175+ 
176+version_check "${REQ_VER_PATH}"
177+exit 0
Ascripts/package/package.py+706-0
@@ -0,0 +1,706 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
12+ 
13+import os
14+import sys
15+import argparse
16+import traceback
17+import csv
18+from argparse import Namespace
19+from collections import namedtuple
20+from datetime import datetime, timezone
21+from functools import partial
22+from itertools import chain
23+from typing import Dict, Iterator, List, Set, Tuple, TextIO
24+import shutil
25+ 
26+from common.py.utils import pkg_utils
27+from common.py.filelist import (
28+ FileItem, FileList, check_filelist, create_file_item, generate_filelist,
29+ get_transform_nested_path_func,
30+)
31+from common.py.packer import (
32+ PackageName, create_makeself_pkg_params_factory, create_run_package_command, exec_pack_cmd
33+)
34+from common.py.pkg_parser import (
35+ ParseOption, XmlConfig, parse_xml_config, get_cann_version_info
36+)
37+from common.py.utils.pkg_utils import (
38+ CONFIG_SCRIPT_PATH, CompressError, ContainAsteriskError, DELIVERY_PATH, FAIL,
39+ FilelistError, GenerateFilelistError, PackageNameEmptyError, SUCCESS, TOP_DIR,
40+ UnknownOperateTypeError, path_join
41+)
42+from common.py.utils.funcbase import invoke, pipe
43+from common.py.utils.comm_log import CommLog
44+ 
45+ 
46+def get_comments(package_name: PackageName) -> str:
47+ """获取run包注释。"""
48+ comments = '_'.join(
49+ [package_name.chip_name.upper(), package_name.func_name.upper(), 'RUN_PACKAGE']
50+ )
51+ return f'"{comments}"'
52+ 
53+ 
54+def get_compress_cmd(delivery_dir: str,
55+ pkg_args: Namespace,
56+ xml_config: XmlConfig) -> str:
57+ """获取makeself压缩命令"""
58+ suffix = xml_config.package_attr.get('suffix')
59+ if suffix == "run":
60+ package_name = PackageName(xml_config.package_attr, pkg_args, xml_config.version)
61+ factory = create_makeself_pkg_params_factory(
62+ pkg_args.pkg_output_dir, package_name.getvalue(), get_comments(package_name)
63+ )
64+ params = factory(pkg_args.makeself_dir, xml_config.package_attr, pkg_args.independent_pkg)
65+ pack_cmd, err_msg = create_run_package_command(params)
66+ if err_msg:
67+ CommLog.cilog_error(err_msg)
68+ CommLog.cilog_error("create_run_command failed!")
69+ raise CompressError(package_name.getvalue())
70+ if pkg_args.independent_pkg:
71+ exec_pack_cmd(delivery_dir, pack_cmd, package_name.getvalue())
72+ else:
73+ CommLog.cilog_error("the repack type '%s' is not support!", suffix)
74+ sys.exit(FAIL)
75+ try:
76+ makeself_dir = os.path.join(TOP_DIR, "build/makeself.txt")
77+ with open(makeself_dir, 'w') as f:
78+ f.write(pack_cmd)
79+ except Exception as exception:
80+ CommLog.cilog_error(f"save makeself.txt failed!{str(exception)}")
81+ sys.exit(FAIL)
82+ return package_name.getvalue()
83+ 
84+ 
85+def make_parse_option(args_: argparse.Namespace) -> ParseOption:
86+ """创建解析参数。"""
87+ 
88+ return ParseOption(
89+ args_.os_arch, args_.pkg_version,
90+ args_.build_type,
91+ args_.package_check,
92+ args_.ext_name
93+ )
94+ 
95+ 
96+PrivatePackageOption = namedtuple(
97+ 'PrivatePackageOption',
98+ [
99+ 'os_arch', 'package_suffix', 'not_in_name', 'pkg_version', 'ext_name',
100+ 'chip_name', 'func_name', 'version_dir', 'disable_multi_version', 'suffix'
101+ ]
102+)
103+ 
104+ 
105+class PackageOption(PrivatePackageOption):
106+ """打包配置参数。"""
107+ __slots__ = () # 优化内存,避免创建 __dict__
108+ 
109+ def __new__(cls, *package_option_args, **kwargs):
110+ return super().__new__(cls, *package_option_args, **kwargs)
111+ 
112+ 
113+def generate_info_content(target_conf, ext_name) -> List[str]:
114+ """生成info内容。"""
115+ 
116+ def toolchain_llvm_config() -> Iterator[Tuple[str, str]]:
117+ if 'llvm' in ext_name:
118+ yield 'toolchain', 'llvm'
119+ 
120+ content_list = [
121+ f'{key}={value}'
122+ for key, value in chain(
123+ target_conf['content'].items(), toolchain_llvm_config()
124+ )
125+ ]
126+ return content_list
127+ 
128+ 
129+def generate_version_header_content(target_conf) -> Iterator[str]:
130+ """生成version_header内容。"""
131+ guard_name = target_conf['value'].replace('.', '_').upper()
132+ yield f'#ifndef {guard_name}'
133+ yield f'#define {guard_name}'
134+ yield ''
135+ for name, value in target_conf['content'].items():
136+ if name.endswith('_VERSION'):
137+ version_infos = get_cann_version_info(name, value)
138+ for version_name, version_value in version_infos:
139+ yield f'#define {version_name} {version_value}'
140+ else:
141+ yield f'#define {name} {value}'
142+ yield ''
143+ yield f'#endif /* {guard_name} */'
144+ yield ''
145+ 
146+ 
147+def generate_customized_file(target_conf, ext_name):
148+ filepath = os.path.join(TOP_DIR, "build", target_conf.get('value'))
149+ 
150+ generator = target_conf.get('generator', 'info')
151+ if generator == 'version_header':
152+ content_list = generate_version_header_content(target_conf)
153+ else:
154+ content_list = generate_info_content(target_conf, ext_name)
155+ 
156+ file_content = '\n'.join(content_list)
157+ try:
158+ with open(filepath, 'w') as file:
159+ file.write(file_content)
160+ except Exception as ex:
161+ CommLog.cilog_error(f"generate customized file {filepath} failed: {ex}!")
162+ return FAIL
163+ 
164+ return SUCCESS
165+ 
166+ 
167+def get_module(target_config) -> str:
168+ """获取配置模块。"""
169+ module = target_config.get('module', 'NA')
170+ return module if module else 'NA'
171+ 
172+ 
173+def get_operation(operation, target_config) -> str:
174+ """获取操作类型。"""
175+ if operation in ('copy', 'move') and target_config.get('entity') == 'true':
176+ return 'copy_entity'
177+ return operation
178+ 
179+ 
180+def get_permission(target_config) -> str:
181+ """获取配置权限。"""
182+ return target_config.get('install_mod', 'NA')
183+ 
184+ 
185+def get_owner_group(target_config) -> str:
186+ """获取配置属主。"""
187+ # install_own的可能值为$username:$usergroup
188+ # 防止变量在install_common_parser.sh中,被eval展开,添加\转义$
189+ # 由于awk会消耗1个\,所以需要2个转义符
190+ return target_config.get('install_own', 'NA').replace('$', '\\\\$')
191+ 
192+ 
193+def get_install_type(target_config) -> str:
194+ """获取安装类型。"""
195+ return target_config.get('install_type', 'NA')
196+ 
197+ 
198+def get_softlink(target_config) -> List[str]:
199+ """获取配置软链。"""
200+ softlink_str = target_config.get('install_softlink')
201+ if not softlink_str:
202+ return []
203+ return softlink_str.split(';')
204+ 
205+ 
206+def get_feature(target_config) -> Set[str]:
207+ """获取配置特性。"""
208+ return target_config['feature']
209+ 
210+ 
211+def get_chip(target_config) -> Set[str]:
212+ """获取配置芯片。"""
213+ return target_config['chip']
214+ 
215+ 
216+def get_configurable(target_config) -> str:
217+ """获取配置是否为配置文件。"""
218+ return target_config.get('configurable', 'FALSE')
219+ 
220+ 
221+def get_hash_value(target_config) -> str:
222+ """获取配置哈希值。"""
223+ return target_config.get('hash', 'NA')
224+ 
225+ 
226+def get_block(target_config) -> str:
227+ """获取配置块信息。"""
228+ return target_config.get('name', 'NA')
229+ 
230+ 
231+def get_pkg_inner_softlink(target_config) -> List[str]:
232+ """获取配置包内软链。"""
233+ softlink_str = target_config.get('pkg_inner_softlink')
234+ if not softlink_str:
235+ return []
236+ return softlink_str.split(';')
237+ 
238+ 
239+def parse_install_info(infos: List,
240+ operate_type,
241+ filter_key) -> Iterator[FileItem]:
242+ """根据配置解析生成安装信息。"""
243+ for target_config in infos:
244+ target_name = get_target_name(target_config)
245+ if target_config.get("optional") == 'true' and operate_type in ('copy', 'move'):
246+ path = os.path.join(TOP_DIR, DELIVERY_PATH, target_config.get('dst_path'))
247+ vaule = os.path.join(TOP_DIR, DELIVERY_PATH, target_config.get('dst_path'), target_name)
248+ if not os.path.exists(path):
249+ continue
250+ if not os.path.exists(vaule):
251+ continue
252+ if operate_type in ('copy', 'move'):
253+ relative_path_in_pkg = os.path.join(target_config.get('dst_path'), target_name)
254+ relative_install_path = path_join(target_config.get('install_path'), target_name)
255+ is_dir = target_config.get('is_dir', False)
256+ elif operate_type == 'mkdir':
257+ relative_path_in_pkg = 'NA'
258+ relative_install_path = target_config.get('value')
259+ is_dir = False
260+ elif operate_type == 'del':
261+ relative_path_in_pkg = 'NA'
262+ relative_install_path = path_join(target_config.get('install_path'), target_name)
263+ is_dir = False
264+ else:
265+ raise UnknownOperateTypeError(f"unknown operate type {operate_type}")
266+ 
267+ if relative_install_path is None:
268+ continue
269+ 
270+ install_type = get_install_type(target_config)
271+ if any(key in install_type for key in filter_key):
272+ is_in_docker = 'TRUE'
273+ else:
274+ is_in_docker = 'FALSE'
275+ 
276+ file_item = create_file_item(
277+ get_module(target_config),
278+ get_operation(operate_type, target_config),
279+ relative_path_in_pkg,
280+ relative_install_path,
281+ is_in_docker,
282+ get_permission(target_config),
283+ get_owner_group(target_config),
284+ install_type,
285+ get_softlink(target_config),
286+ get_feature(target_config),
287+ 'N',
288+ get_configurable(target_config),
289+ get_hash_value(target_config),
290+ get_block(target_config),
291+ get_pkg_inner_softlink(target_config),
292+ get_chip(target_config),
293+ is_dir,
294+ )
295+ 
296+ yield file_item
297+ 
298+ 
299+def execute_repack_process(xml_config: XmlConfig,
300+ delivery_dir: str,
301+ pkg_args: Namespace,
302+ package_name: PackageName = None,
303+ package_option: PackageOption = None):
304+ """
305+ 功能描述: 执行打包流程(拷贝--->签名--->打包)
306+ 返回值: SUCCESS/FAIL
307+ """
308+ release_dir = os.path.join(
309+ delivery_dir, xml_config.default_config.get('name', 'default'))
310+ # 生成自定义文件
311+ for item in xml_config.generate_infos:
312+ if generate_customized_file(item, package_option.ext_name):
313+ return FAIL
314+ 
315+ # 校验包中文件或目录大小
316+ if pkg_args.check_size == "True":
317+ limit_list, tag = processing_csv_file(
318+ release_dir, package_name.func_name, package_name.chip_name, pkg_args.build_type
319+ )
320+ if not tag:
321+ return FAIL
322+ if limit_list:
323+ abspath = os.path.abspath(release_dir)
324+ replace_path = abspath + "/"
325+ result = check_add_dir(replace_path, abspath, limit_list)
326+ if not result:
327+ return FAIL
328+ try:
329+ package_name = get_compress_cmd(pkg_args.pkg_output_dir, pkg_args, xml_config)
330+ except CompressError:
331+ return FAIL
332+ 
333+ CommLog.cilog_info("package %s generate filelist.csv and makeself cmd successfully!",
334+ package_name)
335+ return SUCCESS
336+ 
337+ 
338+def check_path_is_conflict(xml_config):
339+ """
340+ 功能描述: 检查打包时安装路径与软连接路径是否冲突
341+ 参数: xml_config
342+ 返回值: SUCCESS/FAIL
343+ """
344+ install_path_list = set()
345+ pkg_softlink_list = set()
346+ for item in xml_config.package_content_list:
347+ value_list = item.get('value').split('/')
348+ target_name = value_list[-1] if value_list[-1] else value_list[-2]
349+ if item.get('install_path'):
350+ install_path_list.add(
351+ os.path.join(item['install_path'], target_name)
352+ )
353+ if item.get('pkg_inner_softlink'):
354+ pkg_softlink = item.get('pkg_inner_softlink')
355+ pkg_softlink_list.add(pkg_softlink)
356+ if install_path_list & pkg_softlink_list:
357+ CommLog.cilog_info('intersection:{}'.format(install_path_list & pkg_softlink_list))
358+ CommLog.cilog_info('path conflicting: pkg_inner_softlink dir equals install_path!!')
359+ return FAIL
360+ return SUCCESS
361+ 
362+ 
363+def checksum_value(limit_value, release_dir):
364+ """
365+ 功能描叙: 校验传入的文件或目录大小是否合格
366+ 参数:
367+ limit_value: limit.csv中的一行数据如[compiler/bin, 3976, 110%]
368+ 返回值: True/False
369+ """
370+ path = os.path.join(release_dir, limit_value[1])
371+ if len(limit_value) >= 7:
372+ try:
373+ max_value = int(limit_value[4])
374+ except ValueError:
375+ CommLog.cilog_error("{0} configuration is not standard., Please check limit.csv.".format(path))
376+ return True
377+ else:
378+ CommLog.cilog_error("{0} configuration is less than four, Please check limit.csv.".format(path))
379+ return True
380+ if not os.path.exists(path):
381+ CommLog.cilog_warning("{0} doesn't exist, Please check limit.csv.".format(path))
382+ return True
383+ size = 0
384+ for root, dirs, files in os.walk(path):
385+ size += os.path.getsize(root)
386+ for f in files:
387+ filepath = os.path.join(root, f)
388+ if os.path.islink(filepath):
389+ continue
390+ if not os.path.exists(filepath):
391+ continue
392+ size += os.path.getsize(os.path.join(root, f))
393+ if size == 0:
394+ size = os.path.getsize(path)
395+ if size > max_value * 1024:
396+ CommLog.cilog_error(f"\n{path} size {size} bytes exceeds maximum {max_value * 1024} bytes")
397+ return False
398+ return True
399+ 
400+ 
401+def processing_csv_file(release_dir, package_name, chip_name, build_type):
402+ """
403+ 功能描叙: 处理limit.csv文件数据
404+ 返回值: [],True/[],False
405+ """
406+ ret = True
407+ limit_list = []
408+ product = os.path.basename(os.path.dirname(release_dir))
409+ limit_path = os.path.join(pkg_utils.TOP_SOURCE_DIR, CONFIG_SCRIPT_PATH, "common/limit.csv")
410+ if not os.path.exists(limit_path):
411+ CommLog.cilog_warning("{0} doesn't exist.".format(limit_path))
412+ return limit_list, ret
413+ with open(limit_path, "r") as file:
414+ reader = csv.reader(file)
415+ next(reader)
416+ for data in reader:
417+ if not data:
418+ CommLog.cilog_warning("The limit.csv file contains empty lines.")
419+ continue
420+ if is_match_line(package_name, chip_name, product, build_type, data):
421+ if data[1][-1] == "/":
422+ limit_list.append(data[1][:-1])
423+ else:
424+ limit_list.append(data[1])
425+ res = checksum_value(data, release_dir)
426+ if not res:
427+ ret = False
428+ return limit_list, ret
429+ 
430+ 
431+def is_match_line(package_name, chip_name, product, build_type, data):
432+ return package_name == data[0] and chip_name == data[5] and product == data[6] and build_type == data[7].lower()
433+ 
434+ 
435+def check_add_dir(package_path, dirs, limit_list, ret=True):
436+ """
437+ 功能描述: 校验新增目录
438+ 参数: path, limit_list
439+ 返回值: False/True
440+ """
441+ for limit_path in limit_list:
442+ if dirs == os.path.join(os.path.split(dirs)[0], limit_path):
443+ return ret
444+ for dir_file in os.listdir(dirs):
445+ path = os.path.join(dirs, dir_file)
446+ relative_path = path.replace(package_path, "")
447+ if os.path.isfile(path) and relative_path not in limit_list:
448+ CommLog.cilog_error("{0} is not in limit.csv file and is newly added.".format(path))
449+ ret = False
450+ elif os.path.isdir(path) and relative_path not in limit_list:
451+ ret = check_add_dir(package_path, path, limit_list, ret)
452+ return ret
453+ 
454+ 
455+def get_target_name(target_conf) -> str:
456+ """获取目标名。"""
457+ rename = target_conf.get('rename')
458+ if rename:
459+ return rename
460+ 
461+ value_list = target_conf.get('value').split('/')
462+ target_name = value_list[-1] if value_list[-1] else value_list[-2]
463+ return target_name
464+ 
465+ 
466+def gen_file_install_list(xml_config: XmlConfig,
467+ filter_key) -> Tuple[FileList, FileList]:
468+ """生成filelist列表。"""
469+ file_install_list = []
470+ 
471+ dir_filelist = parse_install_info(
472+ xml_config.dir_install_list, 'mkdir', filter_key
473+ )
474+ move_filelist = parse_install_info(
475+ xml_config.move_content_list, 'move', filter_key
476+ )
477+ pkg_filelist = parse_install_info(
478+ xml_config.package_content_list, 'copy', filter_key
479+ )
480+ gen_filelist = parse_install_info(
481+ xml_config.generate_infos, 'copy', filter_key
482+ )
483+ # file_info中配置为文件夹,这里是被展开的文件,则需要单独删除
484+ del_filelist = parse_install_info(
485+ xml_config.expand_content_list, 'del', filter_key
486+ )
487+ collect_filelist = list(chain(dir_filelist, move_filelist, pkg_filelist, gen_filelist))
488+ collect_filelist = list(xml_config.packer_config.fill_is_common_path(collect_filelist))
489+ all_filelist = list(chain(collect_filelist, del_filelist))
490+ for file_item in all_filelist:
491+ file_install_list.append(file_item)
492+ 
493+ return file_install_list, []
494+ 
495+ 
496+def generate_filelist_file_by_xml_config(xml_config: XmlConfig,
497+ filter_key: List[str],
498+ package_check: bool):
499+ """生成文件列表文件。"""
500+ check_move = xml_config.package_attr.get('use_move', False)
501+ transform_nested_path_func = get_transform_nested_path_func(
502+ xml_config.package_attr.get('parallel') or check_move
503+ )
504+ check_features = xml_config.package_attr.get('check_features', False)
505+ 
506+ file_install_list, [] = invoke(
507+ pipe(
508+ gen_file_install_list,
509+ partial(map, transform_nested_path_func),
510+ tuple,
511+ ),
512+ xml_config, filter_key
513+ )
514+ generate_filelist(file_install_list, 'filelist.csv')
515+ # 先生成再检查,有利于问题定位
516+ check_filelist(file_install_list, check_features, check_move)
517+ 
518+ 
519+def get_pkg_xml_relative_path(pkg_args: Namespace) -> str:
520+ """获取包配置文件相对路径。"""
521+ 
522+ def parts():
523+ yield CONFIG_SCRIPT_PATH
524+ yield pkg_args.pkg_name
525+ if pkg_args.chip_scenes:
526+ yield pkg_args.chip_scenes
527+ # 可以通過build_rule指定xml_file,而且优先级高于默认值
528+ if pkg_args.xml_file:
529+ yield pkg_args.xml_file
530+ else:
531+ yield f'{pkg_args.pkg_name}.xml'
532+ 
533+ return os.path.join(*parts())
534+ 
535+ 
536+def write_config_inc_var(name: str, package_attr: Dict, file: TextIO):
537+ """向config.inc文件写入变量。"""
538+ if name in package_attr:
539+ value = str(package_attr[name]).lower()
540+ file.write(f"{name.upper()}={value}\n")
541+ 
542+ 
543+def generate_config_inc(package_attr: Dict):
544+ """生成config.inc文件。"""
545+ if 'parallel' not in package_attr and 'parallel_limit' not in package_attr and 'use_move' not in package_attr:
546+ return
547+ year = datetime.now(timezone.utc).year
548+ config_inc = os.path.join(TOP_DIR, "build", 'config.inc')
549+ header = [
550+ '#!/bin/sh\n',
551+ '#----------------------------------------------------------------------------\n',
552+ f'# Copyright Huawei Technologies Co., Ltd. 2023-{year}. All rights reserved.\n',
553+ '#----------------------------------------------------------------------------\n',
554+ '\n',
555+ ]
556+ if os.path.isfile(config_inc):
557+ os.chmod(config_inc, 0o700)
558+ with open(config_inc, 'w', encoding='utf-8') as file:
559+ file.writelines(header)
560+ write_config_inc_var('parallel', package_attr, file)
561+ write_config_inc_var('parallel_limit', package_attr, file)
562+ write_config_inc_var('use_move', package_attr, file)
563+ 
564+ os.chmod(config_inc, 0o500)
565+ 
566+ 
567+def main(pkg_name='', xml_file='', main_args=None):
568+ """
569+ 功能描述: 执行打包流程(解析配置--->生成文件列表--->执行拷贝/打包动作)
570+ 参数: pkg_name, os_arch, type
571+ 返回值: SUCCESS/FAIL
572+ """
573+ delivery_dir = os.path.join(TOP_DIR, DELIVERY_PATH)
574+ if not os.path.exists(delivery_dir):
575+ return FAIL
576+ 
577+ config_relative_path = get_pkg_xml_relative_path(main_args)
578+ pkg_xml_file = os.path.join(pkg_utils.TOP_SOURCE_DIR, config_relative_path)
579+ parse_option = make_parse_option(main_args)
580+ 
581+ try:
582+ xml_config = parse_xml_config(
583+ pkg_xml_file, delivery_dir, parse_option, main_args
584+ )
585+ except ContainAsteriskError as ex:
586+ CommLog.cilog_error(f"Value contain '*' in {config_relative_path}. value is '{ex.value}'.")
587+ return FAIL
588+ 
589+ if pkg_name in ['driver', 'firmware']:
590+ filter_key = ['all', 'docker']
591+ elif pkg_name in ['aicpu_kernels_device', 'aicpu_kernels_host']:
592+ filter_key = []
593+ else:
594+ filter_key = ['all', 'run']
595+ 
596+ # 生成filelist.csv安装列表文件
597+ try:
598+ generate_filelist_file_by_xml_config(
599+ xml_config, filter_key,
600+ main_args.package_check or xml_config.package_attr.get('package_check')
601+ )
602+ except PackageNameEmptyError:
603+ CommLog.cilog_error(f'package name is empty in {xml_file}, please check it')
604+ return FAIL
605+ except GenerateFilelistError as ex:
606+ CommLog.cilog_error(f'generate filelist {ex.filename} failed!', )
607+ return FAIL
608+ except FilelistError as ex:
609+ CommLog.cilog_error('check filelist error! %s', str(ex))
610+ return FAIL
611+ 
612+ generate_config_inc(xml_config.package_attr)
613+
614+ if main_args.independent_pkg:
615+ src_file_path = os.path.join(TOP_DIR, "build", "filelist.csv")
616+ dst_file_path = os.path.join(main_args.pkg_output_dir, "share", "info", main_args.pkg_name, "script")
617+ shutil.copy(src_file_path, dst_file_path)
618+ 
619+ package_option = PackageOption(
620+ main_args.os_arch, main_args.package_suffix, main_args.not_in_name, main_args.pkg_version, main_args.ext_name,
621+ chip_name=main_args.chip_name, func_name=main_args.func_name, version_dir=main_args.version_dir,
622+ disable_multi_version=main_args.disable_multi_version, suffix=main_args.suffix)
623+ 
624+ package_name = PackageName(xml_config.package_attr, main_args, xml_config.version)
625+ 
626+ # 检查install_path与pkg_inner_softlink路径是否冲突,若冲突则报错
627+ if check_path_is_conflict(xml_config) == FAIL:
628+ return FAIL
629+ 
630+ # 生成打包命令
631+ return execute_repack_process(xml_config, delivery_dir, main_args,
632+ package_name=package_name, package_option=package_option)
633+ 
634+ 
635+def args_parse():
636+ """
637+ 功能描述 : 脚本入参解析
638+ 参数 : 调用脚本的传参
639+ 返回值 : 解析后的参数值
640+ """
641+ parser = argparse.ArgumentParser(
642+ description='This script is for package repack processing.')
643+ parser.add_argument('-c', '--chip_scenes', metavar='chip_scenes', required=False, dest='chip_scenes', nargs='?',
644+ const='',
645+ default='', help='This parameter define chip id for package.')
646+ parser.add_argument('-n', '--pkg_name', metavar='pkg_name', required=False,
647+ help='This parameter define pkg_name for config_xml.')
648+ parser.add_argument('-o', '--os_arch', metavar='os_arch', required=False, dest='os_arch', nargs='?', const='',
649+ default=None, help="This parameter define the package's os_arch")
650+ parser.add_argument('-t', '--type', metavar='type', required=False, dest='type', nargs='?', const='',
651+ default='repack', help="This parameter define this script's function")
652+ parser.add_argument('-i', '--not_in_name', metavar='not_in_name', required=False, dest='not_in_name', nargs='?',
653+ const='',
654+ default='', help="This parameter define the package's name not contain the element")
655+ parser.add_argument('-v', '--pkg_version', metavar='pkg_version', required=False, dest='pkg_version', nargs='?',
656+ const='',
657+ default='', help="This parameter define the version for package.")
658+ parser.add_argument('-e', '--ext_name', metavar='ext_name', required=False, dest='ext_name', nargs='?', const='',
659+ default='', help="This parameter define the package's ext_name")
660+ parser.add_argument('--package_suffix', nargs='?', const='none',
661+ default='none', help="This parameter define the package suffix, debug or none")
662+ parser.add_argument('--suffix', metavar='suffix', required=False, dest='suffix', nargs='?', const='',
663+ default=None, help="This parameter define the package suffix, for example such as tar.gz")
664+ parser.add_argument('-b', '--build_type', metavar='build_type', required=False, dest='build_type', nargs='?',
665+ const='',
666+ default='debug', help="This parameter define release type of package")
667+ parser.add_argument('-x', '--xml', metavar='xml_file', required=False, dest='xml_file', nargs='?', const='',
668+ default='', help="This parameter define xml file")
669+ parser.add_argument('--chip_name', metavar='chip_name', required=False, dest='chip_name', nargs='?', const=None,
670+ default=None,
671+ help="This parameter define package chip name, has higher priority than chip name in xml")
672+ parser.add_argument('--func_name', metavar='func_name', required=False, dest='func_name', nargs='?', const=None,
673+ default=None,
674+ help="This parameter define package func name, has higher priority than func name in xml")
675+ parser.add_argument('--source_root', metavar='source_root', required=False, dest='source_root', nargs='?', const='',
676+ help='source root dir.')
677+ parser.add_argument('--makeself_dir', metavar='makeself_dir', required=False, dest='makeself_dir',
678+ nargs='?', const='', help='makeself dir.')
679+ parser.add_argument('--independent_pkg', action='store_true', help='Independent pkg.')
680+ parser.add_argument('--pkg-output-dir', default='', help='Package dirpath.')
681+ parser.add_argument('--version_dir', nargs='?', const='', default='', help='Set version dir.')
682+ parser.add_argument('--tag', metavar='tag', nargs='?', const='', default='')
683+ parser.add_argument('--disable-multi-version', action='store_true', help='Disable multi version.')
684+ # 检查打包配置
685+ parser.add_argument('--package-check', action='store_true', help='check package config.')
686+ parser.add_argument('--check_size', nargs='?', const='', default='', help="Check the size of a file or directory.")
687+ parser.add_argument('--pkg-name-style', metavar='pkg_name_style', default='common', help='Package name style.')
688+ return parser.parse_args()
689+ 
690+ 
691+if __name__ == "__main__":
692+ CommLog.cilog_info("%s", " ".join(sys.argv))
693+ args = args_parse()
694+ try:
695+ if args.source_root:
696+ pkg_utils.TOP_SOURCE_DIR = args.source_root
697+ if args.build_type == '':
698+ args.build_type = 'debug'
699+ else:
700+ args.build_type = args.build_type.lower()
701+ status = main(args.pkg_name, args.xml_file, main_args=args)
702+ except Exception as e:
703+ CommLog.cilog_error("exception is occurred (%s)!", e)
704+ CommLog.cilog_info("%s", traceback.format_exc())
705+ status = FAIL
706+ sys.exit(status)
Asrc/CMakeLists.txt+97-0
@@ -0,0 +1,97 @@
1+##############################################################################
2+# src/CMakeLists.txt
3+#
4+# 功能:
5+# 直接创建 ops_fft 动态库
6+# 各算子通过 register_operator() 函数注册源文件
7+##############################################################################
8+ 
9+# 引入算子注册函数
10+include(${CMAKE_SOURCE_DIR}/cmake/func.cmake)
11+ 
12+##############################################################################
13+# 创建 ops_fft 动态库
14+##############################################################################
15+ 
16+# 创建一个占位源文件(用于满足 CMake 的最低要求)
17+file(WRITE ${CMAKE_BINARY_DIR}/ops_fft_stub.cpp "// Stub file for ops_fft library")
18+ 
19+# 创建动态库目标(使用占位源文件,后续通过 register_operator 添加实际源文件)
20+add_library(${OPS_FFT} SHARED ${CMAKE_BINARY_DIR}/ops_fft_stub.cpp)
21+ 
22+# 设置库的版本属性
23+set_target_properties(${OPS_FFT} PROPERTIES
24+ VERSION ${PROJECT_VERSION}
25+ SOVERSION 1
26+ OUTPUT_NAME "cann_ops_fft" # 生成文件名:libcann_ops_fft.so
27+)
28+ 
29+# 设置公共包含目录
30+target_include_directories(${OPS_FFT} PUBLIC
31+ ${CMAKE_SOURCE_DIR}/include
32+ ${CMAKE_SOURCE_DIR}/src
33+ ${ASCEND_HOME_PATH}/include # acl.h 等核心头文件
34+ ${ASCEND_HOME_PATH}/pkg_inc/op_common/ # ASCEND 公共操作(kernel_operator.h 等)
35+ ${ASCEND_HOME_PATH}/pkg_inc/base/ # ASCEND 基础类型
36+ ${ASCEND_HOME_PATH}/pkg_inc/ # ASCEND 头文件
37+)
38+ 
39+# 设置编译选项
40+target_compile_options(${OPS_FFT} PRIVATE
41+ $<$<COMPILE_LANGUAGE:ASC>:--npu-arch=${ASCEND_NPU_ARCH}>
42+)
43+ 
44+# 链接 CANN tiling 平台库 (提供 GetCoreNumAiv 等函数)
45+target_link_libraries(${OPS_FFT} PRIVATE
46+ tiling_api
47+)
48+ 
49+##############################################################################
50+# 添加各算子子目录
51+##############################################################################
52+ 
53+message(STATUS "==========================================")
54+message(STATUS "Registering operators...")
55+message(STATUS "==========================================")
56+ 
57+foreach(OP ${BUILD_OPERATORS})
58+ if(EXISTS ${CMAKE_SOURCE_DIR}/src/${OP}/CMakeLists.txt)
59+ add_subdirectory(${OP})
60+ message(STATUS "Added operator directory: ${OP}")
61+ else()
62+ message(WARNING "Operator ${OP} missing CMakeLists.txt, skipping")
63+ endif()
64+endforeach()
65+ 
66+get_target_property(LIB_TARGET_SOURCE ${OPS_FFT} SOURCES)
67+if (NOT LIB_TARGET_SOURCE OR LIB_TARGET_SOURCE STREQUAL "")
68+ message(FATAL_ERROR "No sources added to cann_ops_fft target")
69+endif()
70+ 
71+set_source_files_properties(
72+ ${LIB_TARGET_SOURCE}
73+ PROPERTIES LANGUAGE ASC
74+)
75+ 
76+message(STATUS "==========================================")
77+message(STATUS "Operator registration completed")
78+message(STATUS "==========================================")
79+message(STATUS "")
80+ 
81+##############################################################################
82+# 安装规则
83+##############################################################################
84+ 
85+# 安装库文件到 ops_fft/lib64/
86+install(TARGETS ${OPS_FFT}
87+ LIBRARY DESTINATION ${OPS_FFT_LIB_INSTALL_DIR}
88+ RUNTIME DESTINATION bin
89+)
90+ 
91+# 安装公共头文件到 ops_fft/include/
92+install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/
93+ DESTINATION ${OPS_FFT_INC_INSTALL_DIR}
94+ FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp"
95+)
96+ 
97+message(STATUS "Operator library 'ops_fft' configured successfully")
Atests/CMakeLists.txt+68-0
@@ -0,0 +1,68 @@
1+##############################################################################
2+# tests/CMakeLists.txt
3+#
4+# 自动扫描所有算子测试并生成 all_tests.cpp
5+##############################################################################
6+ 
7+# 只在 BUILD_TESTING 开启时执行
8+if(BUILD_TESTING)
9+ 
10+ # 只扫描已启用算子的测试头文件(相对于 src 目录)
11+ set(TEST_HEADERS "")
12+ foreach(OP ${BUILD_OPERATORS})
13+ set(test_header "${OP}/tests/${OP}_test.h")
14+ # 检查文件是否存在
15+ if(EXISTS "${CMAKE_SOURCE_DIR}/src/${test_header}")
16+ list(APPEND TEST_HEADERS ${test_header})
17+ endif()
18+ endforeach()
19+ 
20+ message(STATUS "找到测试头文件: ${TEST_HEADERS}")
21+ 
22+ # 生成 include 语句列表(不带 src/ 前缀)
23+ set(TEST_INCLUDES "")
24+ foreach(test_header ${TEST_HEADERS})
25+ set(TEST_INCLUDES "${TEST_INCLUDES}#include \"${test_header}\"\n")
26+ endforeach()
27+ 
28+ # 配置生成 all_tests.cpp
29+ configure_file(
30+ ${CMAKE_CURRENT_SOURCE_DIR}/all_tests.cpp.in
31+ ${CMAKE_CURRENT_BINARY_DIR}/all_tests.cpp
32+ @ONLY
33+ )
34+ 
35+ # 收集测试源文件
36+ set(TEST_SOURCES "")
37+ foreach(OP ${BUILD_OPERATORS})
38+ set(test_cpp "${CMAKE_SOURCE_DIR}/src/${OP}/tests/${OP}_test.cpp")
39+ if(EXISTS ${test_cpp})
40+ list(APPEND TEST_SOURCES ${test_cpp})
41+ endif()
42+ endforeach()
43+ 
44+ # 创建测试可执行文件
45+ add_executable(all_ops_test
46+ ${CMAKE_CURRENT_BINARY_DIR}/all_tests.cpp
47+ ${CMAKE_CURRENT_SOURCE_DIR}/test_common.cpp
48+ ${TEST_SOURCES}
49+ )
50+ 
51+ # 设置包含目录
52+ target_include_directories(all_ops_test PRIVATE
53+ ${CMAKE_SOURCE_DIR}/include
54+ ${CMAKE_SOURCE_DIR}/src
55+ ${CMAKE_CURRENT_SOURCE_DIR}
56+ ${ASCEND_HOME_PATH}/include
57+ )
58+ 
59+ # 链接库
60+ target_link_libraries(all_ops_test PRIVATE
61+ ${OPS_FFT}
62+ ${ASCEND_HOME_PATH}/lib64/libascendcl.so
63+ ${ASCEND_HOME_PATH}/lib64/libacl_rt.so
64+ )
65+ 
66+ message(STATUS "已配置测试目标: all_ops_test")
67+ 
68+endif()
Atests/all_tests.cpp.in+53-0
@@ -0,0 +1,53 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software; you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * You should refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+/**
12+ * @file all_tests.cpp
13+ * @brief 主测试入口 - 自动生成文件
14+ */
15+ 
16+#include "test_common.h"
17+#include <iostream>
18+ 
19+// 包含所有算子的测试头文件
20+@TEST_INCLUDES@
21+ 
22+int main() {
23+ std::cout << "========================================" << std::endl;
24+ std::cout << " ops-fft 算子测试套件" << std::endl;
25+ std::cout << "========================================" << std::endl;
26+ std::cout << std::endl;
27+ 
28+ // 初始化 ACL
29+ aclrtStream stream = nullptr;
30+ int ret = OpsFftTest::ACLManager::init(stream);
31+ if (ret != 0) {
32+ std::cerr << "Failed to initialize ACL" << std::endl;
33+ return 1;
34+ }
35+ 
36+ // 运行所有测试
37+ auto& tests = OpsFftTest::TestRegistry::get_tests();
38+ for (const auto& test_entry : tests) {
39+ OpsFftTest::TestStats local_stats;
40+ TEST_PRINT_HEADER(test_entry.name);
41+ test_entry.func(stream, local_stats);
42+ TEST_PRINT_RESULT(test_entry.name, local_stats);
43+ std::cout << std::endl;
44+ }
45+ 
46+ // 打印全局结果
47+ TEST_PRINT_GLOBAL_RESULT();
48+ 
49+ // 清理 ACL
50+ OpsFftTest::ACLManager::finalize(stream);
51+ 
52+ return (OpsFftTest::g_global_stats.failed == 0) ? 0 : 1;
53+}
Atests/test_common.cpp+62-0
@@ -0,0 +1,62 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software; you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * You should refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+/**
12+ * @file test_common.cpp
13+ * @brief 公共测试框架实现 - ACL 初始化和全局测试统计
14+ */
15+ 
16+#include "test_common.h"
17+ 
18+namespace OpsFftTest {
19+ 
20+// 全局测试统计定义
21+TestStats g_global_stats;
22+ 
23+// ACL 初始化
24+int ACLManager::init(aclrtStream& stream) {
25+ // 初始化 ACL
26+ aclError ret = aclInit(nullptr);
27+ if (ret != ACL_SUCCESS) {
28+ std::cerr << "aclInit failed: " << ret << std::endl;
29+ return -1;
30+ }
31+ 
32+ // 设置设备
33+ ret = aclrtSetDevice(0);
34+ if (ret != ACL_SUCCESS) {
35+ std::cerr << "aclrtSetDevice failed: " << ret << std::endl;
36+ aclFinalize();
37+ return -1;
38+ }
39+ 
40+ // 创建流
41+ ret = aclrtCreateStream(&stream);
42+ if (ret != ACL_SUCCESS) {
43+ std::cerr << "aclrtCreateStream failed: " << ret << std::endl;
44+ aclrtResetDevice(0);
45+ aclFinalize();
46+ return -1;
47+ }
48+ 
49+ return 0;
50+}
51+ 
52+// ACL 清理
53+void ACLManager::finalize(aclrtStream& stream) {
54+ if (stream != nullptr) {
55+ aclrtDestroyStream(stream);
56+ stream = nullptr;
57+ }
58+ aclrtResetDevice(0);
59+ aclFinalize();
60+}
61+ 
62+} // namespace OpsFftTest
Atests/test_common.h+184-0
@@ -0,0 +1,184 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software; you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * You should refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+/**
12+ * @file test_common.h
13+ * @brief 公共测试框架 - 提供测试统计、断言宏和 ACL 初始化功能
14+ */
15+ 
16+#ifndef CANN_OPS_FFT_TEST_COMMON_H
17+#define CANN_OPS_FFT_TEST_COMMON_H
18+ 
19+#include <iostream>
20+#include <string>
21+#include <vector>
22+#include <cstdlib>
23+#include "acl/acl.h"
24+ 
25+namespace OpsFftTest {
26+ 
27+// 测试结果统计
28+struct TestStats {
29+ int total = 0;
30+ int passed = 0;
31+ int failed = 0;
32+ 
33+ void print(const std::string& name = "") const {
34+ if (!name.empty()) {
35+ std::cout << name << ": ";
36+ }
37+ std::cout << "总测试数=" << total << ", 通过=" << passed << ", 失败=" << failed << std::endl;
38+ }
39+};
40+ 
41+// 全局测试统计(所有算子累计)
42+extern TestStats g_global_stats;
43+ 
44+// 测试用例开始标记
45+#define TEST_CASE_BEGIN(test_name) \
46+ do { \
47+ std::cout << "[RUN] " << test_name << "..." << std::endl; \
48+ } while(0)
49+ 
50+// 测试用例通过标记(自动更新统计)
51+#define TEST_CASE_PASS(local_stats, test_name) \
52+ do { \
53+ (local_stats).total++; \
54+ (local_stats).passed++; \
55+ OpsFftTest::g_global_stats.total++; \
56+ OpsFftTest::g_global_stats.passed++; \
57+ std::cout << "[PASS] " << test_name << std::endl; \
58+ } while(0)
59+ 
60+// 断言宏:失败时打印错误信息并退出,成功时不打印
61+#define TEST_ASSERT(local_stats, condition, error_msg) \
62+ do { \
63+ if (!(condition)) { \
64+ (local_stats).total++; \
65+ (local_stats).failed++; \
66+ OpsFftTest::g_global_stats.total++; \
67+ OpsFftTest::g_global_stats.failed++; \
68+ std::cerr << " [ERROR] " << error_msg << std::endl; \
69+ std::exit(1); \
70+ } \
71+ } while(0)
72+ 
73+// 精确数组比较宏(无容差,高效 - 使用 std::equal)
74+#define TEST_ASSERT_ARRAY_EQ(local_stats, actual, expected, size, error_msg) \
75+ do { \
76+ bool all_match = std::equal((expected), (expected) + (size), (actual)); \
77+ if (!all_match) { \
78+ auto iter = std::mismatch((expected), (expected) + (size), (actual)); \
79+ size_t mismatch_idx = std::distance((expected), iter.first); \
80+ std::cerr << " [ERROR] " << error_msg << " at index " << mismatch_idx << std::endl; \
81+ } \
82+ TEST_ASSERT((local_stats), all_match, error_msg); \
83+ } while(0)
84+ 
85+// 容差数组比较宏(浮点数,带容差)
86+#define TEST_ASSERT_ARRAY_NEAR(local_stats, actual, expected, size, tol, error_msg) \
87+ do { \
88+ bool all_match = true; \
89+ size_t first_mismatch = 0; \
90+ for (size_t _i = 0; _i < static_cast<size_t>(size); ++_i) { \
91+ if (std::abs((actual)[_i] - (expected)[_i]) > (tol)) { \
92+ all_match = false; \
93+ first_mismatch = _i; \
94+ break; \
95+ } \
96+ } \
97+ if (!all_match) { \
98+ std::cerr << " [ERROR] " << error_msg << " at index " << first_mismatch << std::endl; \
99+ } \
100+ TEST_ASSERT((local_stats), all_match, error_msg); \
101+ } while(0)
102+ 
103+// 测试抬头宏
104+#define TEST_PRINT_HEADER(op_name) \
105+ do { \
106+ std::cout << "========================================" << std::endl; \
107+ std::cout << " " << op_name << "算子单元测试" << std::endl; \
108+ std::cout << "========================================" << std::endl; \
109+ std::cout << std::endl; \
110+ } while(0)
111+ 
112+// 测试结果宏(包含抬头和结尾)
113+#define TEST_PRINT_RESULT(op_name, local_stats) \
114+ do { \
115+ std::cout << std::endl; \
116+ std::cout << "========================================" << std::endl; \
117+ std::cout << " " << op_name << "算子测试结果" << std::endl; \
118+ std::cout << "========================================" << std::endl; \
119+ (local_stats).print(#op_name); \
120+ std::cout << "========================================" << std::endl; \
121+ } while(0)
122+ 
123+// 打印单个测试结果宏(用于循环中,name 是字符串变量)
124+#define TEST_PRINT_RESULT_NAME(name, local_stats) \
125+ do { \
126+ std::cout << std::endl; \
127+ (local_stats).print(name); \
128+ } while(0)
129+ 
130+// 打印全局测试结果宏
131+#define TEST_PRINT_GLOBAL_RESULT() \
132+ do { \
133+ std::cout << std::endl; \
134+ std::cout << "========================================" << std::endl; \
135+ std::cout << " 全局测试结果" << std::endl; \
136+ std::cout << "========================================" << std::endl; \
137+ OpsFftTest::g_global_stats.print("全部算子"); \
138+ std::cout << "========================================" << std::endl; \
139+ } while(0)
140+ 
141+// ACL 管理
142+class ACLManager {
143+public:
144+ static int init(aclrtStream& stream);
145+ static void finalize(aclrtStream& stream);
146+};
147+ 
148+// 测试函数类型
149+using TestFunc = void(*)(aclrtStream, TestStats&);
150+ 
151+// 测试注册表
152+struct TestRegistry {
153+ struct TestEntry {
154+ const char* name;
155+ TestFunc func;
156+ };
157+ 
158+ static std::vector<TestEntry>& get_tests() {
159+ static std::vector<TestEntry> tests;
160+ return tests;
161+ }
162+ 
163+ static void register_test(const char* name, TestFunc func) {
164+ get_tests().push_back({name, func});
165+ }
166+};
167+ 
168+// 自动注册宏
169+#define REGISTER_OP_TEST(op_name) \
170+ namespace op_name##Test { \
171+ void run_all_tests(aclrtStream, OpsFftTest::TestStats&); \
172+ namespace { \
173+ struct Registrar { \
174+ Registrar() { \
175+ OpsFftTest::TestRegistry::register_test(#op_name, run_all_tests); \
176+ } \
177+ }; \
178+ static Registrar registrar; \
179+ } \
180+ }
181+ 
182+} // namespace OpsFftTest
183+ 
184+#endif // CANN_OPS_FFT_TEST_COMMON_H
Aversion.cmake+28-0
@@ -0,0 +1,28 @@
1+# -----------------------------------------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# -----------------------------------------------------------------------------------------------------------
10+ 
11+set_package(ops_fft VERSION "9.0.0")
12+ 
13+# 编译依赖
14+set_build_dependencies(runtime ">=8.5")
15+set_build_dependencies(opbase ">=8.5")
16+set_build_dependencies(metadef ">=8.5")
17+set_build_dependencies(ge-compiler ">=8.5")
18+set_build_dependencies(bisheng-compiler ">=8.5")
19+set_build_dependencies(asc-devkit ">=8.5")
20+ 
21+# 运行时依赖
22+set_run_dependencies(runtime ">=8.5")
23+set_run_dependencies(opbase ">=8.5")
24+set_run_dependencies(metadef ">=8.5")
25+set_run_dependencies(bisheng-compiler ">=8.5")
26+set_run_dependencies(asc-devkit ">=8.5")
27+set_run_dependencies(asc-tools ">=8.5")
28+set_run_dependencies(ops-legacy ">=8.5")