已合并
create ops-fft #2
tianqiguang创建于 3月12日
create ops-fft #2
已合并
共 75 个文件变更+18545-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 | ||
| @@ -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 "==========================================") | ||
| @@ -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分配给您,跟踪协助解决问题。 | ||
| @@ -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) | ||
| @@ -1,2 +1,216 @@ | |||
| 1 | # ops-fft | 1 | # 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) 获取技术文章 | ||
| @@ -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------) | | ||
| @@ -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 "$@" | ||
| @@ -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() | ||
| @@ -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() | ||
| @@ -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}") | ||
| @@ -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() | ||
| @@ -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}") | ||
| @@ -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}") | ||
| @@ -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) | 介绍项目完整的目录结构和各目录/文件的作用 | | ||
| @@ -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) | ||
| @@ -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 | +> **说明**:不同算子的交付件可能有差异,请以实际目录为准。 | ||
| @@ -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 | +``` | ||
| @@ -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) | ||
| @@ -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) | ||
| @@ -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` | ||
| @@ -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) | ||
| @@ -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):编译部署并调用算子 | ||
| @@ -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 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +/* 导出宏定义 */ | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +extern "C" { | ||
| 31 | + | ||
| 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 | + | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | + | ||
| 49 | + | ||
| @@ -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) | ||
| @@ -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) | ||
| @@ -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 | +# ---------------------------------------------------------------------------- | ||
| @@ -0,0 +1,2 @@ | |||
| 1 | +CUSTOM_DATA_PATH=$HOME/Ascend/latest/data/ | ||
| 2 | +CUSTOM_CONF_PATH=$HOME/Ascend/latest/conf/ | ||
| @@ -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 | +# ---------------------------------------------------------------------------- | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | + | ||
| 173 | + def dir_install_list(self): | ||
| 174 | + return self._collect_list('dir_install_list') | ||
| 175 | + | ||
| 176 | + | ||
| 177 | + def move_content_list(self): | ||
| 178 | + return self._collect_list('move_files') | ||
| 179 | + | ||
| 180 | + | ||
| 181 | + def expand_content_list(self): | ||
| 182 | + return self._collect_list('expand_content_list') | ||
| 183 | + | ||
| 184 | + | ||
| 185 | + def package_content_list(self): | ||
| 186 | + return self._collect_list('package_content_list') | ||
| 187 | + | ||
| 188 | + | ||
| 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 | + ) | ||
| @@ -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 | + | ||
| 23 | + def cilog_get_timestamp(): | ||
| 24 | + return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()) | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + def cilog_print_element(cilog_element): | ||
| 28 | + print("["+cilog_element+"]", end=' ') | ||
| 29 | + return | ||
| 30 | + | ||
| 31 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | ||
| @@ -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_) | ||
| @@ -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)) | ||
| @@ -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 | + | ||
| 58 | +class Version: | ||
| 59 | + """版本号。""" | ||
| 60 | + | ||
| 61 | + def __init__(self, version): | ||
| 62 | + self.version = version | ||
| 63 | + | ||
| 64 | + | ||
| 65 | + def match(cls, input_str): | ||
| 66 | + """输入字符串是否匹配版本号模式。""" | ||
| 67 | + m = re.match(r'[.a-zA-Z0-9]+$', input_str) | ||
| 68 | + return bool(m) | ||
| 69 | + | ||
| 70 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 225 | + def _sort_versions(cls, versions: List) -> bool: | ||
| 226 | + """排序版本序列。""" | ||
| 227 | + versions.sort(key=cls._sort_key) | ||
| 228 | + return True | ||
| 229 | + | ||
| 230 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 307 | + def match(cls, filepath: Union[Path, str]) -> bool: | ||
| 308 | + """文件路径是否匹配版本信息文件。""" | ||
| 309 | + return str(filepath).endswith('.xml') | ||
| 310 | + | ||
| 311 | + | ||
| 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) | ||
| @@ -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 | +} | ||
| @@ -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) | ||
| @@ -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 | +} | ||
| @@ -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 | +} | ||
| @@ -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 | +} | ||
| @@ -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 | + | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | + | ||
| @@ -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 | +} | ||
| @@ -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 | +} | ||
| @@ -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 | +} | ||
| @@ -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 | +} | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | + | ||
| 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> | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | + | ||
| 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> | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | + | ||
| 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> | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | + | ||
| 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> | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | + | ||
| 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> | ||
| @@ -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) | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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 | ||
| @@ -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) | ||
| @@ -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") | ||
| @@ -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() | ||
| @@ -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 | +} | ||
| @@ -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 | + | ||
| 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 | ||
| @@ -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 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 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 | + | ||
| 46 | + do { \ | ||
| 47 | + std::cout << "[RUN] " << test_name << "..." << std::endl; \ | ||
| 48 | + } while(0) | ||
| 49 | + | ||
| 50 | +// 测试用例通过标记(自动更新统计) | ||
| 51 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 125 | + do { \ | ||
| 126 | + std::cout << std::endl; \ | ||
| 127 | + (local_stats).print(name); \ | ||
| 128 | + } while(0) | ||
| 129 | + | ||
| 130 | +// 打印全局测试结果宏 | ||
| 131 | + | ||
| 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 | + | ||
| 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 | + | ||
| @@ -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") | ||