Commit c0705977 authored by wangkaixiong's avatar wangkaixiong 🚴🏼
Browse files

init

parent d3982d85
# 1 基于Anaconda的DCU使用示例:
## 1.1. 安装Anaconda;
[Anaconda地址](https://www.anaconda.com/download)
## 1.2. 使用DCU在Pytorch推理Resnet50分类
### 1.2.1. 创建虚拟环境, 设置pip下载源为国内:
```bash
conda create -n dcu_test python=3.10
conda activate dcu_test
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
```
### 1.2.2. 从开发者社区的下载torch、torchvision;
[**DAS生态包下载地址**](https://cancon.hpccube.com:65024/4/main/)
1. 下载torch、torchvision的whl文件到本地;
2. `pip install *.whl`;
3. 验证`torch`是否安装成功;
```shell
python -c "import torch;print(torch.cuda.is_available());print(torch.cuda.device_count())"
```
### 1.2.3. 执行resetnet50分类的推理代码:
```shell
git clone http://developer.hpccube.com/codes/wangkx1/torch_inference_resnet50.git
cd torch_inference_resnet50
python torch_verify.py
```
# 2 基于Docker使用DCU
DCU开发者社区光源镜像介绍:
[https://sourcefind.cn/#/service-list](https://sourcefind.cn/#/service-list)
光源可以查询到基于多种DTK版本的安装的深度学习基础镜像、大模型推理框架(vllm、lmdeploy、fastllm等)镜像、通用模型推理框架镜像(migraphx、AITemplate等)镜像;
## 2.1. 安装Docker
参考当前操作系统的版本号,自行安装docker
查看当前操作系统版本号:
```bash
cat /etc/os-release
```
## 2.2. 启动容器
基于镜像创建的容器可提供开箱即用的基于DCU的深度学习运行环境:
### 2.2.1. 前置条件
1. 安装DCU加速卡,并完成其对应驱动的安装;
2. 正确安装docker;
### 2.2.2. 拉取镜像
```bash
docker pull image.sourcefind.cn:5000/dcu/admin/base/pytorch:1.0-ubuntu20.04-dtk24.04.1-py3.8
```
### 2.2.3. 启动容器命令
```bash
docker run -it \
--network=host \
--ipc=host \
--shm-size=16G \
--device=/dev/kfd \
--device=/dev/mkfd \
--device=/dev/dri \
-v /opt/hyhal:/opt/hyhal \
-v your_path:/workspace \
--group-add video \
--cap-add=SYS_PTRACE \
--security-opt seccomp=unconfined \
--name=dcu_test \
image.sourcefind.cn:5000/dcu/admin/base/pytorch:1.0-ubuntu20.04-dtk24.04.1-py3.8 \
/bin/bash
注:
(1)若出现libhsa-runtime相关报错,启动参数请加上-v /opt/hyhal:/opt/hyhal*;若物理机无/opt/hyhal,请下载hyhal并解压放置容器/opt/下;*
(2)参数解释:
-it # i:打开容器标准输入,t:分配一个伪终端
--network=host # 连接网络(none|host|自定义网络...)
--ipc=host # 设置IPC模式(none|shareable|host...)
--shm-size=24G # 设置/dev/shm大小
--device=/dev/kfd # 指定访问设备(DCU需要添加/dev/kfd、/dev/mkfd、/dev/dri)
--device=/dev/mkfd
--device=/dev/dri
-v /opt/hyhal:/opt/hyhal # dtk23.10以上版本镜像需要-v挂载物理机目录/opt/hyhal
-v your_path:/workspace # 挂载工作目录
--group-add video # 设置用户附加组(普通用户使用DCU需要)
--cap-add=SYS_PTRACE # 添加权限(SYS_PTRACE|NET_ADMIN...)
--security-opt seccomp=unconfined # 安全配置(seccomp=unconfined|label=disable...)
--name=dcu_test # 容器名称
image.sourcefind.cn:5000/dcu/admin/base/custom:alphafold2-2.3.2-dtk23.10-py38 # 所需镜像
/bin/bash # 容器内启动bash
```
## 2.3. 基于容器执行resetnet50分类的推理代码
### 2.3.1. 验证`torch`是否安装成功;
```shell
python -c "import torch;print(torch.cuda.is_available());print(torch.cuda.device_count())"
```
如果不可用, 从开[发者社区](https://cancon.hpccube.com:65024/4/main/)下载安装torch等你需要的深度学习依赖包;
1. 下载torch、torchvision的whl文件到本地;
2. `pip install *.whl`;
### 2.3.2. 执行resetnet50分类的推理代码:
```shell
git clone http://developer.hpccube.com/codes/wangkx1/torch_inference_resnet50.git
cd torch_inference_resnet50
python torch_verify.py
```
# hy-smi 使用介绍
## hy-smi 命令输出介绍
系统终端输入`hy-smi`得到如下输出:
![hy-smi输出](./imgs/hy_smi.png)
输出内容的每一列的说明
- DCU:0-7 是卡的序号索引
- Temp:DCU卡当前运行的温度
- AvgPwr:平均功耗
- Perf:运行的性能模式
- PwrCap:额定功耗
- VARM%:显存占用率
- DCU%:核心利用率
- Mode:默认模式即为`Normal`,不建议设置其他模式(会影响性能)。
## 常用用法:
- 查看显卡名字:`hy-smi --showproductname`
- 查看当前DCU卡上运行进程占用的资源情况:`hy-smi --showpids`
- 查看指定进程在DCU卡上的资源占用情况: `hy-smi --showpiddcus`
- 查看驱动版本号:`hy-smi --showdriverversion`
- 查看`vbios`版本号:`hy-smi -v`
- 查看具体显存占用情况: `hy-smi --showmeminfo vram`
## 更多用法
更多用法请使用 `hy-smi -h`查看学习使用;
\ No newline at end of file
# 1 从NV的GPU迁移到DCU
## 1.1. 构建DCU基础环境
参考第一部分 `构建DCU基础环境`, 完成 DCU 基础环境构建
## 1.2. 替换深度学习算法包
对于依赖cuda的深度学习算法包,需要替换为光合开发者社区的版本;
开发者社区:[https://developer.hpccube.com/tool](https://developer.hpccube.com/tool)
![AI生态包下载地址](./imgs/das.png)
<!-- <center><img src="./imgs/das.png" alt="AI生态包下载地址" style="zoom:50%;" /></center> -->
**手动下载其中对应算法包的whl文件到您的服务器,安装轮子**:
参考如下步骤:
> 注意: 替换算法包的时候,算法包的版本需要和DTK对应;
1. pip 配置国内安装源
```bash
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip install pip -U
```
2. 安装轮子
```bash
pip install ***.whl
```
## 1.3. DCU 适配案例:
**DCU开发者社区光源ModelZoo介绍**(可以快速查询所需的DCU算法模型,根据Readme进行构建DCU环境,一键运行所需的算法模型):
[https://sourcefind.cn/#/model-zoo/list](https://sourcefind.cn/#/model-zoo/list)
光源可以查询到基于多种DCU适配的各个AI技术领域的算法模型以及算法框架,如bert、yolo、resnet、Qwen、Llama、vllm、lmdpeloy等等;
**Examples:**
- [YOLOv10](https://sourcefind.cn/#/model-zoo/1802637886774013954)
- [Rep_Vit](https://sourcefind.cn/#/model-zoo/1805170476575846402)
- [alphafold2_jax](https://sourcefind.cn/#/model-zoo/1712346117256200194)
- [llama3](https://sourcefind.cn/#/model-zoo/1782218524112154626)
- [stablediffusion_v2.1](https://sourcefind.cn/#/model-zoo/1793173002231443458)
- [Qwen1.5](https://sourcefind.cn/#/model-zoo/1793160576505180161)
\ No newline at end of file
## 下载中心:
- [**驱动下载地址**](https://cancon.hpccube.com:65024/6/main) → latest 驱动→ rock-xxx-xxx.aio.run
- [**DTK下载地址**](https://cancon.hpccube.com:65024/1/main) → latest → 对应的操作系统 → DTK-version-OS-version-x86_64.tar.gz
- [**工具包地址(DCU直通、Kubernets插件、HyQual压力测试、工具包文档)**](https://cancon.hpccube.com:65024/5/main)
- [**DAS生态包下载地址**](https://cancon.hpccube.com:65024/4/main/)
- [**光源地址**](https://sourcefind.cn/#/main-page)
\ No newline at end of file
FAQ-cuda以及hip移植常见问题处理经验
### 问题一、纹理内存报错:
1. /data/wkx/develop/ppl.cv/src/ppl/cv/cuda/warp.hpp:31:8: error: no
template named \'texture\'
2. static texture\<float4, cudaTextureType2D,
3. \^
4. /data/wkx/develop/ppl.cv/src/ppl/cv/cuda/warp.hpp/data/wkx/develop/ppl.cv/src/ppl/cv/cuda/warp.hpp::3131::88::
5. error: error: no template named \'texture\'no template named
\'texture\'
6. 7. static texture\<float4, cudaTextureType2D,
8. static texture\<float4, cudaTextureType2D,
#### **解决方法:**
**CUDA 的 texture 类型在较新版本(CUDA 12
及以上)中已被弃用或移除**。旧版 CUDA(如 CUDA 10 或 11)中可以使用
texture\<T, \...\> 这种全局变量声明方式,但在 **CUDA 12+
中,这种语法不再支持**,必须改用 **cudaTextureObject\_t +
cudaResourceDesc/cudaTextureDesc** 的方式来创建纹理对象
使用DCU的cuda-11.8编译老旧代码即可顺利通过;
### 问题二、 launch bounds (256) 报错:
Launch params (1024, 1, 1) are larger than launch bounds (256) for
kernel \_ZL12rms\_norm\_f32ILi1024EEvPKfPfif please add launch\_bounds
to kernel define or use \--gpu-max-threads-per-block recompile program !
#### 解决方法:
解决方法1:
1. 所有的核函数 \_\_global\_\_ 替换为 \_\_global\_\_
\_\_launch\_bounds\_\_(1024)
解决方法2:
nvcc或者hip编译增加: \--gpu-max-threads-per-block=1024
### 问题三、asm 代码,内联汇编代码编译报错;
![4XDLESZEAAQE6](media/image1.png){width="5.763888888888889in"
height="1.8840080927384077in"}
#### 解决方法:
内嵌 PTX 功能开启需要主动加"-fnline-asm-ptx"选项。
![LMVLGSZEABAFG](media/image2.png){width="5.763888888888889in"
height="2.996674321959755in"}
### 问题四、 cuda应用不转码适配找不到 math.h 头文件
![CISLKSZEACABE](media/image3.png){width="5.763888888888889in"
height="1.9728018372703413in"}
#### 解决方法:
cmake 编译中增加的 -isystem /usr/include 与 nvcc
编译器同时使用会存在冲突。
开启打印,关注编译过程的 完整头文件、库文件的依赖,去掉 -isystem
/usr/include 即可编译成功。
make VERBOSE=1 \<project\>
### 问题五、使用开源的pycuda 无法编译 cu文件
#### 解决方法:
参考这个,更改下 compiler.py 适配 hip 编译;
[[https://ontrack.hygon.cn/browse/CSD-10705]{.underline}](https://ontrack.hygon.cn/browse/CSD-10705)
### 问题六、如何针对一个文件夹的cu代码进行转码
详细可以参考:
![ppt](media/image4.png){width="0.1527777777777778in"
height="0.1527777777777778in"}[[DCU应用移植介绍-程顺延]{.underline}](https://www.kdocs.cn/l/cmD2M59DD2vk)
#### 解决方法:
1. hipconvertinplace-perl.sh \<cuda代码文件夹\>
cuda 文件夹下原有的代码,转码后以 org-name.h/cu.prehip
形式存储在当前目录
由于要使用hip编译, 因此所有的 cu 后缀, 修改为 hip 或者 cpp;
### 问题七、hip转码后部分宏定义不规范不会被转换,可能导致出现问题:
#### 解决方法:
- CublasHandleManager.h
1. \#if !defined(ROCM\_SYMLINK\_HIPBLAS\_H)
2. \#error hipblas.h must be included at the very top of any file
including CublasHandleManager.h
3. \#endif
4. 5. 从 CUBLAS\_V2\_H\_ 更改为 ROCM\_SYMLINK\_HIPBLAS\_H
### 问题八、 math\_constants.h 找不到:
#### 解决方法:
DTK的cuda下有 math\_constants.h 会被别的工程依赖;
hip下不存在对应的代码,可以直接拷贝 math\_constants.h 到工程中使用;
math\_constants.h 仅仅是一些数学值的定义;
### 问题九、转码后部分hip核函数不识别 min:
#### 解决方法:
EddyMatrixKernels.cpp 中不支持 min 的问题解决
1. \_\_global\_\_ void QR(// Input
2. const float \*K, // Row-first matrices to decompose
3. unsigned int m, // Number of rows of K
4. unsigned int n, // Number of columns of K
5. unsigned int nmat, // Number of matrices
6. // Output
7. float \*Qt, // nmat mxm Q matrices
8. float \*R) // nmat mxn R matrices
9. {
10. extern \_\_shared\_\_ float scratch\[\];
11. 12. if (blockIdx.x \< nmat && threadIdx.x \< m) {
13. unsigned int id = threadIdx.x;
14. // unsigned int ntpm = min(m,blockDim.x); // Number of threads per
matrix
15. unsigned int ntpm = (m \< blockDim.x) ? m : blockDim.x;
16. float \*v = scratch;
17. float \*w = &scratch\[m\];
18. const float \*lK = &K\[blockIdx.x\*m\*n\];
19. float \*lQt = &Qt\[blockIdx.x\*m\*m\];
20. float \*lR = &R\[blockIdx.x\*m\*n\];
21. qr\_single(lK,m,n,v,w,id,ntpm,lQt,lR);
22. }
23. return;
24. }
### 问题十、使用 DTK-25.04 之后的软件栈编译报头文件错:
#### 解决方法:
尽量尝试使用 -std=c++17\\-std=c++14
### 问题十一、g++ 编译 hipRuntime(hipMalloc、hipMemcpy)等接口代码,编译报错:
#### 解决方法:
编译时增加宏定义,
\_\_HIP\_PLATFORM\_AMD\_\_
链接依赖增加 -l galaxyhip
## 操作系统版本兼容列表
**注意**:
> 使用`iso`镜像安装操作系统时,请勿允许任何操作系统的更新行为, 否则会带来内核版本的升级,导致安装失败; <br>
> 可参考 `构建DCU基础环境` 的 `2.3\3.2` 下的常用操作系统安装步骤之中的锁核操作;
**操作系统**:
开发者社区推荐操作系统:
| 操作系统 | 版本 | 内核 |
| ------- | --- | ---- |
| Centos | 7.6 | 3.10.0-957.el7.x86_64 |
| Centos | 8.5 | 4.18.0-348.el8.x86_64 |
| Rocky | 8.6 | 4.18.0-348.el8.x86_64 |
| Rocky | 9.2 | 4.18.0-348.el8.x86_64 |
| Ubuntu | 20.04.1 | 5.4.0-42-generic |
| Ubuntu | 22.04 | 5.15.0-25-generic |
| NFS | 3.2 | 3.10.0-957.nfs.5.x86_64 |
| NFS | 4.0 | 4.19.113-14.1.nfs4.x86_64 |
| NFS | 4.0-Desktop | 5.4.0-49-generic |
| UOS | 1021e | 4.19.90-2109.1.0.0108.up2.uel20.x86_64 |
| Kylin | v10 SP2 | 4.19.90-24.4.v2101.ky10.x86_64 |
| Anolis | 8.4 | 4.19.91-23.4.an8.x86_64 |
| Anolis | 8.6 | 4.19.91-26.an8.x86_64 |
| openEuler | 22.03 | 5.10.0-60.18.0.50.oe2203.x86_64 |
| BCLinux | 8.2 | 4.19.0-240.23.11.el8_2.bclinux.x86_64 |
[操作系统兼容性列表包含(兼容性等级等)](https://docs.qq.com/sheet/DVHdTZHB3RVZOVENI?tab=dklqmf)
---
## 支持的DCU型号
- Z100
- Z100L
- K100
- K100_AI
## DCU软件介绍:
- **DTK**:
> DCU加速卡软件工具包:包括函数库、编译环境、管理工具、性能分析工具等。
- **DAS (DCU AI Software Stack)**:
> 目前主要以python的whl形式在光合开发者社区进行发布。
- 算子层
- 框架层
- 扩展组件层
## **兼容性(必读)**
### 加速卡与DTK的兼容性
| DCU猩红 | DTK版本 | 注意 |
| ------- | ------- | ------- |
| Z100 | DTK >=21.04 | 推荐使用 DTK >= 23.10 |
| Z100L | DTK >=21.04 | 推荐使用 DTK >= 23.10 |
| K100 | DTK >=23.10 | |
| K100-AI | DTK >=24.04 | |
### DTK和DAS(AI生态包)兼容性
| DTK版本 | DAS版本 | 注意 |
| ------- | ------- | ------- |
|DTK-24.04.1 | DAS1.1 | 见下述 DAS1.1 使用注意 |
|DTK-24.04 | DAS1.0 | |
**注意:**
> 不兼容的版本可能出现严重的环境问题
---
- DAS1.1 使用注意:
- 不支持操作系统`ubuntu18.04`, `Centos7.6` 等 `glibc <= 2.31`的操作系统
- glib 版本查看方式: `ldd --version`
- 如果遇到问题, 建议通过 docker 使用 glibc 高版本的容器系统;
---
欢迎来到 DCU 的环境安装教程!
====================================
资源下载:
-------------
* `驱动下载 <https://cancon.hpccube.com:65024/6/main>`_ → latest 驱动 → rock-xxx-xxx.aio.run
* `DTK下载 <https://cancon.hpccube.com:65024/1/main>`_ → latest → 对应的操作系统 → DTK-version-OS-version-x86_64.tar.gz
* `工具包地址(DCU直通、Kubernets插件、HyQual压力测试、工具包文档) <https://cancon.hpccube.com:65024/5/main>`_
* `DAS生态包下载 <https://cancon.hpccube.com:65024/4/main/>`_
* `光源地址 <https://sourcefind.cn/#/main-page>`_
文档
-------------
.. toctree::
:maxdepth: 2
:caption: 基础介绍
get_started.md
.. toctree::
:maxdepth: 1
:caption: 资源下载地址
download.md
.. toctree::
:maxdepth: 1
:caption: 构建DCU基础环境
./install_dcu_on_os/base_install_intro.md
./install_dcu_on_os/centos.md
./install_dcu_on_os/ubuntu.md
.. toctree::
:maxdepth: 2
:caption: 快速使用 DCU
Anaconda_Docker.md
.. toctree::
:maxdepth: 2
:caption: hy-smi 使用介绍
Hy-SMI.md
.. toctree::
:maxdepth: 2
:caption: 从NV的GPU迁移到DCU
NV_GPU_TO_DCU.md
.. toctree::
:maxdepth: 2
:caption: CUDA 与 HIP 移植常见问题
faq_cuda_hip.md
索引与表格
==================
* :ref:`genindex`
* :ref:`search`
\ No newline at end of file
## 1. 开发者社区 DCU 环境安装手册
该文档主要针对 DCU 加速卡,提供基础软件环境安装部署以及基础测试的参考指导。
建议参考如下文档进行安装DCU基础环境:
[**点击,进入开发者社区环境搭建文档**](https://cancon.hpccube.com:65024/1/main/latest/Document) → DTK 开发环境安装部署手册.pdf
\ No newline at end of file
## **2 DCU基础环境完整教程-Centos7.6**:
### 2.1 **非root用户安装注意事项:**
- 确保非root用户已加入`video`组,以便能够使用DCU。
```shell
# 对于有sudo权限的非root用户
sudo usermod -aG video $USER
# 对于无sudo权限的用户,由root执行
usermod -aG video <userid>
```
### 2.2. **操作系统设置**:
确保启动项中不包含nomodeset选项,如果内核以nomodeset选项启动,则驱动可能无法成功加载。
- 需要保证系统纯净,命令行运行 `lsmod | grep amdgpu` 为空
- 关闭 selinux(可选)
修改`/etc/selinux/config`,设置`SELINUX=disabled`
- 关闭 firewalld(可选)
```bash
systemctl stop firewalld
systemctl disable firewalld
```
### 2.3. **关闭内核自动更新:**
- 编辑`/etc/yum.conf`,在`[main]`部分添加:
```
exclude=kernel*
exclude=centos-release*
```
### 2.4. **更新yum源:**
- 替换为中科大源,针对CentOS 7.6的示例, (注意使用双引号):
```shell
sed -e "s|^mirrorlist=|#mirrorlist=|g" -e "s|^#baseurl=http://mirror.centos.org/centos/\$releasever|baseurl=https://mirrors.ustc.edu.cn/centos-vault/$minorver|g" -i.bak /etc/yum.repos.d/CentOS-*.repo
```
- 替换 `CentOS-CR.repo` 并且 `enable`(安装python3需要)
```shell
sed -i "s|^baseurl=http://mirror.centos.org/centos/\$releasever|baseurl=https://mirrors.ustc.edu.cn/centos-vault/$minorver|g;s|enabled=0|enabled=1|g" /etc/yum.repos.d/CentOS-CR.repo
```
- 对 Centos7 配置 SCLo 源(安装devtoolset需要):
编辑 `/etc/yum.repos.d/CentOS-SCLo.repo` 配置文件;
```shell
vi /etc/yum.repos.d/CentOS-SCLo.repo
```
在 `/etc/yum.repos.d/CentOS-SCLo.repo` 写入以下内容, `Esc + :wq` 保存退出
```shell
[centos-sclo-sclo]
name=CentOS-7 - SCLo sclo
baseurl=https://mirrors.ustc.edu.cn/centos/7/sclo/$basearch/sclo/
gpgcheck=0
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-SCLo
[centos-sclo-rh]
name=CentOS-7 - SCLo rh
baseurl=https://mirrors.ustc.edu.cn/centos/7/sclo/$basearch/rh/
gpgcheck=0
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-SCLo
```
- 对 Centos7 配置 epel 源(安装cmake3需要)
编辑 `/etc/yum.repos.d/epel-7.repo` 配置文件;
```shell
vi /etc/yum.repos.d/epel-7.repo
```
在/etc/yum.repos.d/epel-7.repo写入以下内容, `Esc + :wq` 保存退出;
```shell
[epel]
name=Extra Packages for Enterprise Linux 7 - $basearch
baseurl=http://mirrors.aliyun.com/epel/7/$basearch
failovermethod=priority
enabled=1
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
[epel-debuginfo]
name=Extra Packages for Enterprise Linux 7 - $basearch – Debug
baseurl=http://mirrors.aliyun.com/epel/7/$basearch/debug
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck=0
[epel-source]
name=Extra Packages for Enterprise Linux 7 - $basearch – Source
baseurl=http://mirrors.aliyun.com/epel/7/SRPMS
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck=0
```
- 更新cache
```shell
yum clean all
yum makecache
```
### 2.5. **安装相关依赖:**
- 联网执行以下命令安装必要的依赖包:
```shell
#安装 DCU 加速卡驱动所需的依赖包命令
yum install -y gcc gcc-c++ rpm-build autoconf kernel-devel-$(uname -r) kernel-headers-$(uname -r)
#安装cmake3
yum install -y cmake3
ln -s /usr/bin/cmake3 /usr/bin/cmake
#安装 DTK 开发环境所需的依赖包命令
yum install -y centos-release-scl
yum install -y gcc gcc-c++ gcc-gfortran elfutils elfutils-devel make rpm-build devtoolset-7
yum install -y libbabeltrace-devel libbabeltrace pciutils-devel libpciaccess-devel
yum install -y numactl-devel elfutils-libelf-devel mesa-libGL-devel
yum install -y epel-release cmake3 pciutils-libs pciutils-devel
yum install -y perl-File-Which perl-File-BaseDir perl-File-Copy-Recursive perl-File-Listing java-1.8.0-openjdk
yum install -y git python python-pip python-devel python-wheel python3 python3-pip python3-devel python3-wheel sqlite-devel libibverbs
yum install -y redhat-lsb-core gettext gettext-devel protobuf
yum install -y perl-Digest perl-Digest-MD5 perl-Data-Dumper vim-common curl libcurl libcurl-devel
yum install -y doxygen graphviz texlive texlive-xtab texlive-multirow texlive-sectsty texlive-tocloft
yum install -y texlive-adjustbox deltarpm tcl automake
```
- 离线安装依赖:
<br>
依赖离线包:rpm_DTK2404_centos7.6_3.10.0-957.tar
<br>
链接:https://pan.baidu.com/s/1jnWfddL4lHWVQb3btD34Iw?pwd=wj6q
<br>
提取码:wj6q
<br>
下载好导入服务器,进行解压:
<br>
1. `vi /etc/yum.repos.d/localyum.repo`,输入如下内容,请根据解压的路径,修改baseurl的内容,下面配置是在root路径下解压的依赖包;
```shell
[local-repo]
name=local-repo
baseurl=file:///root/centos7-dtk24.04
enabled=1
gpgcheck=0
```
2. 开始安装离线依赖包
```shell
sudo yum clean all
#以安装cmake3为例
yum --disablerepo="*" --enablerepo="local-repo" install cmake3
```
### 2.6. **校验系统配置:**
| 设备名称 | 设备码 |
| ----- | ----- |
| Z100L | 1d94:55b7 |
| K100 | 1d94:62b7 |
| K100_AI | 1d94:6210 |
- 查看DCU设备
```shell
# Z100L
root@sugontest79:/mnt#lspci -nn |grep -i 55b7
------------------------------------------------------------------------------------------------------
07:00.0 Display controller [0380]: Chengdu Haiguang IC Design Co., Ltd. ZIFANG [1d94:55b7] (rev 01)
0a:00.0 Display controller [0380]: Chengdu Haiguang IC Design Co., Ltd. ZIFANG [1d94:55b7] (rev 01)
------------------------------------------------------------------------------------------------------
# K100
root@sugontest79:/mnt#lspci -nn |grep -i 62b7
------------------------------------------------------------------------------------------------------
07:00.0 Co-processor [0b40]: Chengdu Haiguang IC Design Co., Ltd. KONGMING [1d94:62b7] (rev 01)
0a:00.0 Co-processor [0b40]: Chengdu Haiguang IC Design Co., Ltd. KONGMING [1d94:62b7] (rev 01)
------------------------------------------------------------------------------------------------------
# K100-AI
root@sugontest79:/mnt#lspci -nn |grep -i 6210
------------------------------------------------------------------------------------------------------
07:00.0 Co-processor [0b40]: Chengdu Haiguang IC Design Co., Ltd. KONGMING [1d94:6210] (rev 01)
0a:00.0 Co-processor [0b40]: Chengdu Haiguang IC Design Co., Ltd. KONGMING [1d94:6210] (rev 01)
------------------------------------------------------------------------------------------------------
```
> 注:输出信息可能和截图不完全一致。
- 配置环境变量
<br>
创建文件 `/etc/profile.d/devtoolset-7.sh`,内容如下:
<br>
`source /opt/rh/devtoolset-7/enable`
<br>
退出当前登录会话重新登录,或者执行source /etc/profile.d/devtoolset-7.sh。
### 2.7. **安装驱动:**
**注意:**
> DTK和rock驱动有对应关系,可参考[dcu-环境安装手册](#DCU环境安装手册),推荐安装最新的使用<br>
> -安装驱动之前需要安装基础包,包括cmake、gcc等多种基础依赖包,请先参考`DCU环境安装手册`完成基础环境包的安装。
**驱动下载地址**: [https://cancon.hpccube.com:65024/6/main](https://cancon.hpccube.com:65024/6/main) → latest 驱动→ rock-xxx-xxx.aio.run
1. 安装 DCU 加速卡驱动
```bash
chmod 755 rock-5.7.1-6.2.13-V1.0.1a.aio.run
./rock-5.7.1-6.2.13-V1.0.1a.aio.run
```
2. 如果安装过程更新了`vbios`, 则需要重启机器
```bash
reboot
```
3. 查看验证是否安装成功
```bash
# 出现如下类似结果则安装成功
[root@b04r3n02 ~]# lsmod | grep hydcu
hydcu 1435342 0
hydcu_sched 34432 1 hydcu
hyttm 61919 1 hydcu
hykcl 46567 3 hydcu_sched,hydcu,hyttm
hy_extra 32140 3 hydcu_sched,hydcu,hykcl
amd_iommu_v2 18821 1 hydcu
drm_kms_helper 179394 3 ast,hydcu,hykcl
drm 429744 8 ast,ttm,hydcu,hykcl,hyttm,drm_kms_helper
```
4. 卸载驱动步骤:
<br>
如遇到异常情况或需要更新版本,先执行驱动卸载。
<br>
```shell
rpm -qa | grep rock #查询安装的驱动版本
rmmod hydcu
rpm -e rock-5.7.1-6.2.18-1.x86_64
```
### 2.8. **安装DTK:**
**DTK下载地址**: [https://cancon.hpccube.com:65024/1/main](https://cancon.hpccube.com:65024/1/main) → latest → 对应的操作系统 → DTK-version-OS-version-x86_64.tar.gz
1. 安装
```bash
# 解压安装
tar xvf DTK-24.04.1-CentOS7.6-x86_64.tar.gz -C /opt
# 创建软连接
ln -s /opt/dtk-24.04.1 /opt/dtk
```
3. 设置 DTK 环境变量
> DTK 压缩文件中提供了设置环境变量脚本 env.sh。可以通过 source /opt/dtk/env.sh 的方式临时加载环境变量。为避免多次配置,常用以下方式加载环境变量
```bash
echo "source /opt/dtk/env.sh">> ~/.bashrc
# 激活环境变量
source ~/.bashrc
```
4. 验证 DCU 环境
```bash
# 查看并执行 hy-smi 或者 rocm-smi 指令查看 dcu 基本信息
[root@h01r4n04~]# rocm-smi
# 出现如下内容, 则安装成功
===================System Management Interface =================
==========================================================
DCU Temp AvgPwr Fan Perf PwrCap VRAM% DCU%
0 50.0c 55.0W 0.0% auto 450.0W 0% 0%
1 50.0c 58.0W 0.0% auto 450.0W 0% 0%
2 49.0c 58.0W 0.0% auto 450.0W 0% 0%
3 49.0c 55.0W 0.0% auto 450.0W 0% 0%
==========================================================
======================End of SMI Log========================
```
### 2.9. **验证安装结果:**
1. 使用`rocminfo`命令检查ROCm系统状态
<br>
终端输入如下内容:
<br>
```shell
rocminfo | grep gfx
# 其中Z100/Z100L为gfx906,K100为gfx926,K100_AI为gfx928; 有输出即说明驱动和DTK安装成功
------------------------------------------------------------------------------------------------------------
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
```
<br>
2. 运行`hy-smi`或`rocm-smi`来监控DCU的状态和性能指标;
```shell
# 查看并执行 hy-smi 或者 rocm-smi 指令查看 dcu 基本信息
[root@h01r4n04~]# rocm-smi
# 出现如下内容, 则安装成功
============================ System Management Interface =============================
======================================================================================
DCU Temp AvgPwr Perf PwrCap VRAM% DCU% Mode
0 42.0C 39.0W auto 280.0W 0% 0% Normal
1 41.0C 39.0W auto 280.0W 0% 0% Normal
2 41.0C 36.0W auto 280.0W 0% 0% Normal
3 40.0C 38.0W auto 280.0W 0% 0% Normal
4 40.0C 39.0W auto 280.0W 0% 0% Normal
5 41.0C 41.0W auto 280.0W 0% 0% Normal
6 42.0C 37.0W auto 280.0W 0% 0% Normal
7 41.0C 36.0W auto 280.0W 0% 0% Normal
======================================================================================
=================================== End of SMI Log ===================================
```
## **3 DCU基础环境完整教程-ubuntu**:
### 3.1. **安装系统过程中注意:**
> Ubuntu20.04.1(5.4.0-42-generic)和Ubuntu22.04.1(5.15.0-25-generic)已在 DCU 上进行全量验证,本文以Ubuntu20.04.1(5.4.0-42-generic)安装部署为例,如果是其他版本或者kernel不一致可能导致驱动无效
1. 安装时不要连接网络(直接断掉网线或者在安装系统时将网络disable),否则会自动升级内核(<font color="red">即使选择了不更新操作</font>)。
安装系统时将网络disable;
![disable_net](../imgs/ubuntu/disable_net.png)
2. 安装过程中, 涉及到更新的选项都选择不更新;
### 3.2. **安装完成系统后的配置**
1. 安装完系统后确定内核, 是否符合兼容性, 如果不符合兼容性,
```shell
root@test79:/mnt# uname -r
5.4.0-173-generic
```
建议确认
- 使用的操作系统版本是否在兼容性列表;
- 是否在安装操作系统中禁用网络,选择不更新的相关配置;
2. 确保非root用户已加入`video`组,以便能够使用DCU;
- 确保非root用户已加入`video`组,以便能够使用DCU, shell 命令修改;
```shell
# 对于有sudo权限的非root用户
sudo usermod -aG video $USER
# 对于无sudo权限的用户,由root执行
usermod -aG video <userid>
```
- 或者,或者修改/etc/group文件,添加test用户;
![disable_net](../imgs/ubuntu/render.png)
退出重新登录。
3. 关闭内核自动更新:
- 查看安装内核:
```shell
dpkg --list | grep linux-image
dpkg --list | grep linux-headers
dpkg --list | grep linux-modules
# 输出结果:-----------------------------------------------------------------------------------------------------------
ii linux-image-5.4.0-173-generic 5.4.0-173.191 amd64 Signed kernel image generic
hi linux-image-5.4.0-42-generic 5.4.0-42.46 amd64 Signed kernel image generic
ii linux-image-generic 5.4.0.173.171 amd64 Generic Linux kernel image
```
- 禁止内核更新方法1:
```shell
sudo vi /etc/apt/apt.conf.d/10periodic
sudo vi /etc/apt/apt.conf.d/20auto-upgrades
# 后面部分全部改成 “0”
# 修改后内容 ----------------------------------------
# 10periodic
APT::Periodic::Update-Package-Lists "0";
APT::Periodic::Download-Upgradeable-Packages "0";
APT::Periodic::AutocleanInterval "0";
# 20auto-upgrades
APT::Periodic::Update-Package-Lists "0";
APT::Periodic::Unattended-Upgrade "0";
```
- 禁止内核更新方法2:
直接使用hold参数,固定内核版本:
```shell
sudo apt-mark hold linux-image-5.4.0-42-generic
sudo apt-mark hold linux-headers-5.4.0-42-generic
sudo apt-mark hold linux-modules-extra-5.4.0-42-generic
```
查询 Ubuntu系统被锁定不更新的软件包状态(hold)
```shell
sudo dpkg --get-selections | grep hold
```
### 3.3. **更新国内软件下载源:**
1. 备份源文件:
```shell
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
```
2. 修改源文件sources.list: 将原文件内容全部注释或删掉,添加以下内容;
```shell
# 当前使用阿里源, 如果想使用其他源, 可以自行在网上搜索其他源的配置文件,并替换掉以下内容;
# 注意换源的时候需要和操作系统的版本号作匹配
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
```
如果是ubuntu-22.04可以直接使用如下方式, 更换为华为源;
```shell
# 修改为华为源
sudo sed -i "s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list
sudo sed -i "s@http://.*security.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list
```
3. 更新:
```shell
sudo apt-get update
```
### 3.4. **安装相关依赖:**
- 联网执行以下命令安装必要的依赖包:
```shell
# 安装 DCU 加速卡驱动所需的依赖包命令
sudo apt-get install -y cmake gcc autoconf linux-kernel-headers kernel-package automake linux-modules-extra-`uname -r` linux-image-`uname -r` linux-headers-`uname -r`
# 安装 DTK 开发环境所需的依赖包命令
sudo apt-get install -y make gcc g++ cmake git wget gfortran elfutils libdrm-dev
sudo apt-get install -y kmod libtinfo5 sqlite3 libsqlite3-dev libelf-dev libibverbs1 libgtk2.0-0
sudo apt-get install -y libnuma-dev libgl1-mesa-dev rpm rsync mesa-common-dev apt-utils
sudo apt-get install -y cmake libpci-dev pciutils libpciaccess-dev libbabeltrace-dev pkg-config
sudo apt-get install -y libfile-which-perl libfile-basedir-perl libfile-copy-recursive-perl libfile-listing-perl
sudo apt-get install -y python3 python3-pip python3-dev python3-wheel
sudo apt-get install -y gettext gettext-base libprotobuf-dev tcl
sudo apt-get install -y libio-digest-perl libdigest-md5-file-perl libdata-dumper-simple-perl vim curl libcurlpp-dev
sudo apt-get install -y doxygen graphviz texlive libncurses5 msgpack*
sudo apt install mlocate
```
- 离线安装依赖:
<br>
依赖离线包:deb_DTK2404_Ubuntu20.04.1_5.4.0-42-generic.tar
<br>
链接:https://pan.baidu.com/s/1jnWfddL4lHWVQb3btD34Iw?pwd=wj6q
<br>
提取码:wj6q
<br>
下载好导入服务器,进行解压:
<br>
1. `vi /etc/apt/sources.list.d/myrepo.list`,输入如下内容,注意修改解压的路径,下面配置是在/data路径下解压的;
```shell
vi /etc/apt/sources.list.d/myrepo.list
#输入如下内容,注意修改解压的路径,下面配置是在/data路径下解压的
deb [trusted=yes] file:///data/my-debian-packages ./
#保存退出即可
chmod +r /data
chown -R _apt:root /datahR
chown -R man:root /var/cache/man
#更新系统的apt缓存,使其能够识别并使用新添加的本地私有源
apt update
apt-get install udev
apt install mlocate
```
更新了之后,就可以通过`apt install`安装依赖包了。
### 3.5. **校验系统配置:**
| 设备名称 | 设备码 |
| ----- | ----- |
| Z100L | 1d94:55b7 |
| K100 | 1d94:62b7 |
| K100_AI | 1d94:6210 |
- 查看DCU设备
```shell
# Z100L
root@sugontest79:/mnt#lspci -nn |grep -i 55b7
------------------------------------------------------------------------------------------------------
07:00.0 Display controller [0380]: Chengdu Haiguang IC Design Co., Ltd. ZIFANG [1d94:55b7] (rev 01)
0a:00.0 Display controller [0380]: Chengdu Haiguang IC Design Co., Ltd. ZIFANG [1d94:55b7] (rev 01)
------------------------------------------------------------------------------------------------------
# K100
root@sugontest79:/mnt#lspci -nn |grep -i 62b7
------------------------------------------------------------------------------------------------------
07:00.0 Co-processor [0b40]: Chengdu Haiguang IC Design Co., Ltd. KONGMING [1d94:62b7] (rev 01)
0a:00.0 Co-processor [0b40]: Chengdu Haiguang IC Design Co., Ltd. KONGMING [1d94:62b7] (rev 01)
------------------------------------------------------------------------------------------------------
# K100-AI
root@sugontest79:/mnt#lspci -nn |grep -i 6210
------------------------------------------------------------------------------------------------------
07:00.0 Co-processor [0b40]: Chengdu Haiguang IC Design Co., Ltd. KONGMING [1d94:6210] (rev 01)
0a:00.0 Co-processor [0b40]: Chengdu Haiguang IC Design Co., Ltd. KONGMING [1d94:6210] (rev 01)
------------------------------------------------------------------------------------------------------
```
> 注:输出信息可能和截图不完全一致。
### 3.6. **安装驱动:**
**注意:**
> DTK和rock驱动有对应关系,可参考[dcu-环境安装手册](#DCU环境安装手册),推荐安装最新的使用<br>
> -安装驱动之前需要安装基础包,包括cmake、gcc等多种基础依赖包,请先参考`DCU环境安装手册`完成基础环境包的安装。
**驱动下载地址**: [https://cancon.hpccube.com:65024/6/main](https://cancon.hpccube.com:65024/6/main) → latest 驱动→ rock-xxx-xxx.aio.run
1. 安装 DCU 加速卡驱动
```bash
chmod 755 rock-5.7.1-6.2.13-V1.0.1a.aio.run
./rock-5.7.1-6.2.13-V1.0.1a.aio.run
```
2. 如果安装过程更新了`vbios`, 则需要重启机器
```bash
reboot
```
3. 查看验证是否安装成功
```bash
# 出现如下类似结果则安装成功
[root@b04r3n02 ~]# lsmod | grep hydcu
hydcu 1435342 0
hydcu_sched 34432 1 hydcu
hyttm 61919 1 hydcu
hykcl 46567 3 hydcu_sched,hydcu,hyttm
hy_extra 32140 3 hydcu_sched,hydcu,hykcl
amd_iommu_v2 18821 1 hydcu
drm_kms_helper 179394 3 ast,hydcu,hykcl
drm 429744 8 ast,ttm,hydcu,hykcl,hyttm,drm_kms_helper
```
4. 卸载驱动步骤:
<br>
如遇到异常情况或需要更新版本,先执行驱动卸载。
<br>
```shell
rpm -qa | grep rock #查询安装的驱动版本
rmmod hydcu
rpm -e rock-5.7.1-6.2.18-1.x86_64
```
### 3.7. **安装DTK:**
**DTK下载地址**: [https://cancon.hpccube.com:65024/1/main](https://cancon.hpccube.com:65024/1/main) → latest → 对应的操作系统 → DTK-version-OS-version-x86_64.tar.gz
1. 安装
```bash
# 解压安装
tar xvf DTK-24.04.1-CentOS7.6-x86_64.tar.gz -C /opt
# 创建软连接
ln -s /opt/dtk-24.04.1 /opt/dtk
```
3. 设置 DTK 环境变量
> DTK 压缩文件中提供了设置环境变量脚本 env.sh。可以通过 source /opt/dtk/env.sh 的方式临时加载环境变量。为避免多次配置,常用以下方式加载环境变量
```bash
echo "source /opt/dtk/env.sh">> ~/.bashrc
# 激活环境变量
source ~/.bashrc
```
4. 验证 DCU 环境
```bash
# 查看并执行 hy-smi 或者 rocm-smi 指令查看 dcu 基本信息
[root@h01r4n04~]# rocm-smi
# 出现如下内容, 则安装成功
===================System Management Interface =================
==========================================================
DCU Temp AvgPwr Fan Perf PwrCap VRAM% DCU%
0 50.0c 55.0W 0.0% auto 450.0W 0% 0%
1 50.0c 58.0W 0.0% auto 450.0W 0% 0%
2 49.0c 58.0W 0.0% auto 450.0W 0% 0%
3 49.0c 55.0W 0.0% auto 450.0W 0% 0%
==========================================================
======================End of SMI Log========================
```
### 3.8. **验证安装结果:**
1. 使用`rocminfo`命令检查ROCm系统状态
<br>
终端输入如下内容:
<br>
```shell
rocminfo | grep gfx
# 其中Z100/Z100L为gfx906,K100为gfx926,K100_AI为gfx928; 有输出即说明驱动和DTK安装成功
------------------------------------------------------------------------------------------------------------
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
Name: amdgcn-amd-amdhsa--gfx906:sramecc+:xnack-
```
<br>
2. 运行`hy-smi`或`rocm-smi`来监控DCU的状态和性能指标;
```shell
# 查看并执行 hy-smi 或者 rocm-smi 指令查看 dcu 基本信息
[root@h01r4n04~]# rocm-smi
# 出现如下内容, 则安装成功
============================ System Management Interface =============================
======================================================================================
DCU Temp AvgPwr Perf PwrCap VRAM% DCU% Mode
0 42.0C 39.0W auto 280.0W 0% 0% Normal
1 41.0C 39.0W auto 280.0W 0% 0% Normal
2 41.0C 36.0W auto 280.0W 0% 0% Normal
3 40.0C 38.0W auto 280.0W 0% 0% Normal
4 40.0C 39.0W auto 280.0W 0% 0% Normal
5 41.0C 41.0W auto 280.0W 0% 0% Normal
6 42.0C 37.0W auto 280.0W 0% 0% Normal
7 41.0C 36.0W auto 280.0W 0% 0% Normal
======================================================================================
=================================== End of SMI Log ===================================
```
/* Compatability shim for jQuery and underscores.js.
*
* Copyright Sphinx contributors
* Released under the two clause BSD licence
*/
/**
* small helper function to urldecode strings
*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
*/
jQuery.urldecode = function(x) {
if (!x) {
return x
}
return decodeURIComponent(x.replace(/\+/g, ' '));
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 360px;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar,
aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption, figcaption {
padding: 0.3em;
}
div.figure p.caption span.caption-number,
figcaption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text,
figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
aside.footnote > span,
div.citation > span {
float: left;
}
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0 0.5em;
content: ":";
display: inline-block;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
white-space: nowrap;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
-webkit-user-select: text; /* Safari fallback only */
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-check" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#22863a" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M5 12l5 5l10 -10" />
</svg>
/*!
* clipboard.js v2.0.8
* https://clipboardjs.com/
*
* Licensed MIT © Zeno Rocha
*/
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{container:document.body},i="";return"string"==typeof t?(e=t,n="rtl"===document.documentElement.getAttribute("dir"),(o=document.createElement("textarea")).style.fontSize="12pt",o.style.border="0",o.style.padding="0",o.style.margin="0",o.style.position="absolute",o.style[n?"right":"left"]="-9999px",n=window.pageYOffset||document.documentElement.scrollTop,o.style.top="".concat(n,"px"),o.setAttribute("readonly",""),o.value=e,o=o,r.container.appendChild(o),i=c()(o),a("copy"),o.remove()):(i=c()(t),a("copy")),i};function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var s=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},e=t.action,n=void 0===e?"copy":e,o=t.container,e=t.target,t=t.text;if("copy"!==n&&"cut"!==n)throw new Error('Invalid "action" value, use either "copy" or "cut"');if(void 0!==e){if(!e||"object"!==r(e)||1!==e.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===n&&e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===n&&(e.hasAttribute("readonly")||e.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes')}return t?l(t,{container:o}):e?"cut"===n?f(e):l(e,{container:o}):void 0};function d(t){return(d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function p(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}function y(t,e){return(y=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function h(n){var o=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}();return function(){var t,e=m(n);return t=o?(t=m(this).constructor,Reflect.construct(e,arguments,t)):e.apply(this,arguments),e=this,!(t=t)||"object"!==d(t)&&"function"!=typeof t?function(t){if(void 0!==t)return t;throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}(e):t}}function m(t){return(m=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function v(t,e){t="data-clipboard-".concat(t);if(e.hasAttribute(t))return e.getAttribute(t)}var o=function(){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&y(t,e)}(r,i());var t,e,n,o=h(r);function r(t,e){var n;return function(t){if(!(t instanceof r))throw new TypeError("Cannot call a class as a function")}(this),(n=o.call(this)).resolveOptions(e),n.listenClick(t),n}return t=r,n=[{key:"copy",value:function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{container:document.body};return l(t,e)}},{key:"cut",value:function(t){return f(t)}},{key:"isSupported",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:["copy","cut"],t="string"==typeof t?[t]:t,e=!!document.queryCommandSupported;return t.forEach(function(t){e=e&&!!document.queryCommandSupported(t)}),e}}],(e=[{key:"resolveOptions",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=u()(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget,t=s({action:this.action(e),container:this.container,target:this.target(e),text:this.text(e)});this.emit(t?"success":"error",{action:this.action,text:t,trigger:e,clearSelection:function(){e&&e.focus(),document.activeElement.blur(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(t){return v("action",t)}},{key:"defaultTarget",value:function(t){t=v("target",t);if(t)return document.querySelector(t)}},{key:"defaultText",value:function(t){return v("text",t)}},{key:"destroy",value:function(){this.listener.destroy()}}])&&p(t.prototype,e),n&&p(t,n),r}()},828:function(t){var e;"undefined"==typeof Element||Element.prototype.matches||((e=Element.prototype).matches=e.matchesSelector||e.mozMatchesSelector||e.msMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector),t.exports=function(t,e){for(;t&&9!==t.nodeType;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}},438:function(t,e,n){var u=n(828);function i(t,e,n,o,r){var i=function(e,n,t,o){return function(t){t.delegateTarget=u(t.target,n),t.delegateTarget&&o.call(e,t)}}.apply(this,arguments);return t.addEventListener(n,i,r),{destroy:function(){t.removeEventListener(n,i,r)}}}t.exports=function(t,e,n,o,r){return"function"==typeof t.addEventListener?i.apply(null,arguments):"function"==typeof n?i.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return i(t,e,n,o,r)}))}},879:function(t,n){n.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},n.nodeList=function(t){var e=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===e||"[object HTMLCollection]"===e)&&"length"in t&&(0===t.length||n.node(t[0]))},n.string=function(t){return"string"==typeof t||t instanceof String},n.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},370:function(t,e,n){var f=n(879),l=n(438);t.exports=function(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!f.string(e))throw new TypeError("Second argument must be a String");if(!f.fn(n))throw new TypeError("Third argument must be a Function");if(f.node(t))return c=e,a=n,(u=t).addEventListener(c,a),{destroy:function(){u.removeEventListener(c,a)}};if(f.nodeList(t))return o=t,r=e,i=n,Array.prototype.forEach.call(o,function(t){t.addEventListener(r,i)}),{destroy:function(){Array.prototype.forEach.call(o,function(t){t.removeEventListener(r,i)})}};if(f.string(t))return t=t,e=e,n=n,l(document.body,t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList");var o,r,i,u,c,a}},817:function(t){t.exports=function(t){var e,n="SELECT"===t.nodeName?(t.focus(),t.value):"INPUT"===t.nodeName||"TEXTAREA"===t.nodeName?((e=t.hasAttribute("readonly"))||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),e||t.removeAttribute("readonly"),t.value):(t.hasAttribute("contenteditable")&&t.focus(),n=window.getSelection(),(e=document.createRange()).selectNodeContents(t),n.removeAllRanges(),n.addRange(e),n.toString());return n}},279:function(t){function e(){}e.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o<r;o++)n[o].fn.apply(n[o].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),o=n[t],r=[];if(o&&e)for(var i=0,u=o.length;i<u;i++)o[i].fn!==e&&o[i].fn._!==e&&r.push(o[i]);return r.length?n[t]=r:delete n[t],this}},t.exports=e,t.exports.TinyEmitter=e}},r={},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,{a:e}),e},o.d=function(t,e){for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o(686).default;function o(t){if(r[t])return r[t].exports;var e=r[t]={exports:{}};return n[t](e,e.exports,o),e.exports}var n,r});
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#000000" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="8" y="8" width="12" height="12" rx="2" />
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
</svg>
/* Copy buttons */
button.copybtn {
position: absolute;
display: flex;
top: .3em;
right: .3em;
width: 1.7em;
height: 1.7em;
opacity: 0;
transition: opacity 0.3s, border .3s, background-color .3s;
user-select: none;
padding: 0;
border: none;
outline: none;
border-radius: 0.4em;
/* The colors that GitHub uses */
border: #1b1f2426 1px solid;
background-color: #f6f8fa;
color: #57606a;
}
button.copybtn.success {
border-color: #22863a;
color: #22863a;
}
button.copybtn svg {
stroke: currentColor;
width: 1.5em;
height: 1.5em;
padding: 0.1em;
}
div.highlight {
position: relative;
}
/* Show the copybutton */
.highlight:hover button.copybtn, button.copybtn.success {
opacity: 1;
}
.highlight button.copybtn:hover {
background-color: rgb(235, 235, 235);
}
.highlight button.copybtn:active {
background-color: rgb(187, 187, 187);
}
/**
* A minimal CSS-only tooltip copied from:
* https://codepen.io/mildrenben/pen/rVBrpK
*
* To use, write HTML like the following:
*
* <p class="o-tooltip--left" data-tooltip="Hey">Short</p>
*/
.o-tooltip--left {
position: relative;
}
.o-tooltip--left:after {
opacity: 0;
visibility: hidden;
position: absolute;
content: attr(data-tooltip);
padding: .2em;
font-size: .8em;
left: -.2em;
background: grey;
color: white;
white-space: nowrap;
z-index: 2;
border-radius: 2px;
transform: translateX(-102%) translateY(0);
transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1);
}
.o-tooltip--left:hover:after {
display: block;
opacity: 1;
visibility: visible;
transform: translateX(-100%) translateY(0);
transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1);
transition-delay: .5s;
}
/* By default the copy button shouldn't show up when printing a page */
@media print {
button.copybtn {
display: none;
}
}
// Localization support
const messages = {
'en': {
'copy': 'Copy',
'copy_to_clipboard': 'Copy to clipboard',
'copy_success': 'Copied!',
'copy_failure': 'Failed to copy',
},
'es' : {
'copy': 'Copiar',
'copy_to_clipboard': 'Copiar al portapapeles',
'copy_success': '¡Copiado!',
'copy_failure': 'Error al copiar',
},
'de' : {
'copy': 'Kopieren',
'copy_to_clipboard': 'In die Zwischenablage kopieren',
'copy_success': 'Kopiert!',
'copy_failure': 'Fehler beim Kopieren',
},
'fr' : {
'copy': 'Copier',
'copy_to_clipboard': 'Copier dans le presse-papier',
'copy_success': 'Copié !',
'copy_failure': 'Échec de la copie',
},
'ru': {
'copy': 'Скопировать',
'copy_to_clipboard': 'Скопировать в буфер',
'copy_success': 'Скопировано!',
'copy_failure': 'Не удалось скопировать',
},
'zh-CN': {
'copy': '复制',
'copy_to_clipboard': '复制到剪贴板',
'copy_success': '复制成功!',
'copy_failure': '复制失败',
},
'it' : {
'copy': 'Copiare',
'copy_to_clipboard': 'Copiato negli appunti',
'copy_success': 'Copiato!',
'copy_failure': 'Errore durante la copia',
}
}
let locale = 'en'
if( document.documentElement.lang !== undefined
&& messages[document.documentElement.lang] !== undefined ) {
locale = document.documentElement.lang
}
let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT;
if (doc_url_root == '#') {
doc_url_root = '';
}
/**
* SVG files for our copy buttons
*/
let iconCheck = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-check" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#22863a" fill="none" stroke-linecap="round" stroke-linejoin="round">
<title>${messages[locale]['copy_success']}</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M5 12l5 5l10 -10" />
</svg>`
// If the user specified their own SVG use that, otherwise use the default
let iconCopy = ``;
if (!iconCopy) {
iconCopy = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#000000" fill="none" stroke-linecap="round" stroke-linejoin="round">
<title>${messages[locale]['copy_to_clipboard']}</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="8" y="8" width="12" height="12" rx="2" />
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
</svg>`
}
/**
* Set up copy/paste for code blocks
*/
const runWhenDOMLoaded = cb => {
if (document.readyState != 'loading') {
cb()
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', cb)
} else {
document.attachEvent('onreadystatechange', function() {
if (document.readyState == 'complete') cb()
})
}
}
const codeCellId = index => `codecell${index}`
// Clears selected text since ClipboardJS will select the text when copying
const clearSelection = () => {
if (window.getSelection) {
window.getSelection().removeAllRanges()
} else if (document.selection) {
document.selection.empty()
}
}
// Changes tooltip text for a moment, then changes it back
// We want the timeout of our `success` class to be a bit shorter than the
// tooltip and icon change, so that we can hide the icon before changing back.
var timeoutIcon = 2000;
var timeoutSuccessClass = 1500;
const temporarilyChangeTooltip = (el, oldText, newText) => {
el.setAttribute('data-tooltip', newText)
el.classList.add('success')
// Remove success a little bit sooner than we change the tooltip
// So that we can use CSS to hide the copybutton first
setTimeout(() => el.classList.remove('success'), timeoutSuccessClass)
setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon)
}
// Changes the copy button icon for two seconds, then changes it back
const temporarilyChangeIcon = (el) => {
el.innerHTML = iconCheck;
setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon)
}
const addCopyButtonToCodeCells = () => {
// If ClipboardJS hasn't loaded, wait a bit and try again. This
// happens because we load ClipboardJS asynchronously.
if (window.ClipboardJS === undefined) {
setTimeout(addCopyButtonToCodeCells, 250)
return
}
// Add copybuttons to all of our code cells
const COPYBUTTON_SELECTOR = 'div.highlight pre';
const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR)
codeCells.forEach((codeCell, index) => {
const id = codeCellId(index)
codeCell.setAttribute('id', id)
const clipboardButton = id =>
`<button class="copybtn o-tooltip--left" data-tooltip="${messages[locale]['copy']}" data-clipboard-target="#${id}">
${iconCopy}
</button>`
codeCell.insertAdjacentHTML('afterend', clipboardButton(id))
})
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
/**
* Removes excluded text from a Node.
*
* @param {Node} target Node to filter.
* @param {string} exclude CSS selector of nodes to exclude.
* @returns {DOMString} Text from `target` with text removed.
*/
function filterText(target, exclude) {
const clone = target.cloneNode(true); // clone as to not modify the live DOM
if (exclude) {
// remove excluded nodes
clone.querySelectorAll(exclude).forEach(node => node.remove());
}
return clone.innerText;
}
// Callback when a copy button is clicked. Will be passed the node that was clicked
// should then grab the text and replace pieces of text that shouldn't be used in output
function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
var regexp;
var match;
// Do we check for line continuation characters and "HERE-documents"?
var useLineCont = !!lineContinuationChar
var useHereDoc = !!hereDocDelim
// create regexp to capture prompt and remaining line
if (isRegexp) {
regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)')
} else {
regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)')
}
const outputLines = [];
var promptFound = false;
var gotLineCont = false;
var gotHereDoc = false;
const lineGotPrompt = [];
for (const line of textContent.split('\n')) {
match = line.match(regexp)
if (match || gotLineCont || gotHereDoc) {
promptFound = regexp.test(line)
lineGotPrompt.push(promptFound)
if (removePrompts && promptFound) {
outputLines.push(match[2])
} else {
outputLines.push(line)
}
gotLineCont = line.endsWith(lineContinuationChar) & useLineCont
if (line.includes(hereDocDelim) & useHereDoc)
gotHereDoc = !gotHereDoc
} else if (!onlyCopyPromptLines) {
outputLines.push(line)
} else if (copyEmptyLines && line.trim() === '') {
outputLines.push(line)
}
}
// If no lines with the prompt were found then just use original lines
if (lineGotPrompt.some(v => v === true)) {
textContent = outputLines.join('\n');
}
// Remove a trailing newline to avoid auto-running when pasting
if (textContent.endsWith("\n")) {
textContent = textContent.slice(0, -1)
}
return textContent
}
var copyTargetText = (trigger) => {
var target = document.querySelector(trigger.attributes['data-clipboard-target'].value);
// get filtered text
let exclude = '.linenos';
let text = filterText(target, exclude);
return formatCopyText(text, '>>> |\\.\\.\\. ', true, true, true, true, '', '')
}
// Initialize with a callback so we can modify the text before copy
const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText})
// Update UI with error/success messages
clipboard.on('success', event => {
clearSelection()
temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success'])
temporarilyChangeIcon(event.trigger)
})
clipboard.on('error', event => {
temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure'])
})
}
runWhenDOMLoaded(addCopyButtonToCodeCells)
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment