0
0
0

鸿蒙造物者之路 03 · 铸剑

文章摘要
|

续写章节|沿用「造物者」系列的武侠讲解体,面向电子信息 / 嵌入式方向的新手

=================前情提要===============

老陈给我划了三条底线:4 核 CPU、8 G 内存、100 G 磁盘,外加一句"南向编译必须 Linux"。

他还说今晚是动手之夜——装系统、拉源码、编第一个固件,目标是让那颗蓝灯按我的意志闪烁。

后来我才知道,那一晚我根本没摸到灯。我摸到的是一整晚的报错。

========================================


这一章你会得到什么

🎯 学完这一章,你应该能:

  • ☐ 搭好 Ubuntu 编译环境(虚拟机方案,最安全)
  • ☐ 拿到 OpenHarmony 源码,编出第一个固件
  • ☐ 把固件烧进开发板,并能通过串口看到日志
  • ☐ 记住七个高频坑的解法(这一章的「翻车急救手册」最值钱)

**🧰 需要准备:**100 GB 硬盘(建议 150)、4 核 CPU、8 GB 内存、Hi3861 开发板、能传数据的 USB 线。

**⏱ 预计用时:**4–8 小时。这一章耗时最长,别急。

📊 难度:★★★★★ 劝退墙,几乎每个人都会卡


一、先把话说清楚:两套工具,别搞混

"动手之前,先认清楚你要装的是哪一套东西。"老陈在白板上画了个分叉:

                 ┌──────────────────────────────┐
                 │   鸿蒙开发                     │
                 └───────────────┬───────────────┘
                    ┌────────────┴────────────┐
                    ▼                         ▼
        ┌───────────────────┐     ┌───────────────────┐
        │  南向 · 设备开发    │     │  北向 · 应用开发    │
        ├───────────────────┤     ├───────────────────┤
        │ 系统:Ubuntu       │     │ 系统:Win / Mac    │
        │ IDE:DevEco Device │     │ IDE:DevEco Studio │
        │      Tool(VSCode)│     │                   │
        │ 语言:C / C++      │     │ 语言:ArkTS        │
        │ 产出:固件 .bin     │     │ 产出:应用 .hap     │
        └───────────────────┘     └───────────────────┘

"名字只差一个词,干的完全不是一件事。"老陈特意把两个名字圈出来:

  • DevEco Device Tool —— 南向用的,是 VS Code 的一个插件,跑在 Ubuntu / Windows 上,用来编译烧录调试固件
  • DevEco Studio —— 北向用的,是 IntelliJ IDEA 改的独立 IDE,用来写 ArkTS 应用

"我见过太多新手装错了。"老陈说,“想点灯结果装了 Studio,装完一脸懵:我的 GPIO 在哪儿?”

“那我两个都要装?”

"都要,但不用同一天装。"老陈说,“今晚先把南向这条线打通。北向的 DevEco Studio 等你第 06 章再说。”

二、Ubuntu:第一道坎

2.1 怎么装

“三种方案,你自己选。”

方案 优点 缺点 适合谁
物理机双系统 性能满血,编译最快 装崩了影响 Windows,切换要重启 有闲置电脑 / 胆子大的
虚拟机(VirtualBox / VMware) 安全,崩了删了重来 编译慢,IO 是瓶颈 新手首选
WSL2 跟 Windows 无缝,方便 串口 / USB 支持麻烦,烧录要折腾 有经验的人

“我选虚拟机。”

"明智。"老陈点头,“第一次搞,别拿双系统赌。等哪天你确定要长期做南向了,再考虑上物理机。”

他给了我几条硬性要求:

虚拟机配置(底线,不是推荐值)
─────────────────────────────
CPU      : 4 核(给满,别省)
内存      : 8 GB 起(编译峰值会吃满)
磁盘      : 100 GB 起(建议 150 GB,动态分配也行)
网络      : 必须能访问外网
系统      : Ubuntu 20.04 或 22.04 LTS

“为什么非要 LTS?”

"因为 OpenHarmony 的编译脚本对系统版本敏感。"老陈说,“非 LTS 版本的 glibc、Python 版本可能太新,编译脚本没适配,报错报到你怀疑人生。20.04 和 22.04 是社区验证过最多的两个。”

2.2 装完之后第一件事:换源

“系统装好,第一件事不是装鸿蒙,是换软件源。”

# 备份原来的源
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

# 换成国内镜像(以清华源为例,也可以换阿里/中科大)
sudo sed -i 's|http://.*archive.ubuntu.com|https://mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list
sudo sed -i 's|http://.*security.ubuntu.com|https://mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list

# 更新
sudo apt update && sudo apt upgrade -y

“不换会怎样?”

"不换的话,下载速度几十 KB,你能等到天亮。"老陈说,“这是最容易被忽略、但最浪费时间的一步。”

2.3 装依赖

"然后装这一堆。"老陈甩给我一条命令:

sudo apt install -y \
    git git-lfs curl wget vim \
    build-essential gcc g++ make cmake \
    ninja-build gn python3 python3-pip python3-venv \
    libssl-dev zlib1g-dev libffi-dev \
    unzip zip dosfstools mtools \
    default-jre device-tree-compiler \
    libncurses5-dev

"python3 版本必须是 3.8 以上。"他强调,“检查一下:”

python3 --version
# 输出应该是 Python 3.8.x / 3.9.x / 3.10.x

“如果系统默认是 python3 但 python 命令不存在呢?”

"加个软链接,或者永远用 python3。"老陈说,“OpenHarmony 的脚本大多写的是 python3,所以问题不大。但有些老脚本会调 python,保险起见:”

sudo ln -sf /usr/bin/python3 /usr/bin/python

⚠️ 注意:Ubuntu 20.04 之后官方不建议把 python 指向 python3(可能干扰系统包管理)。如果你只在虚拟机里玩,无所谓;如果是双系统,建议用 python3 显式调用。

2.4 pip 也换源

pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

三、拿源码:三种姿势

"源码怎么拿?"我问。

“三种,推荐第二种。”

姿势一:repo 拉全量(最正规,最慢)

# 1. 装 repo 工具
mkdir -p ~/bin
curl https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 -o ~/bin/repo
chmod +x ~/bin/repo
echo 'export PATH=~/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

# 2. 初始化(以 master 分支为例,实际请选 LTS 版本分支)
mkdir -p ~/openharmony && cd ~/openharmony
repo init -u https://gitee.com/openharmony/manifest.git -b master --no-repo-verify

# 3. 同步(这一步很慢,几小时起步)
repo sync -c
repo forall -c 'git lfs pull'

# 4. 下载预编译工具链
bash build/prebuilts_download.sh

"repo sync 这一步,国内网络可能要四五个小时。"老陈说,“而且中途断一次就得重来。你要是网络不好,直接用第二种。”

姿势二:下发行版压缩包(新手推荐)

“去 OpenHarmony 官网的发布版本页,直接下对应版本的源码压缩包(一般在几 GB 到十几 GB)。下完解压即可,比 repo 快得多,也稳得多。”

# 假设下载的是 code-vX.x.x.tar.gz
tar -zxvf code-vX.x.x.tar.gz -C ~/openharmony
cd ~/openharmony
bash build/prebuilts_download.sh   # 还是要下载预编译工具链

“下载页面在哪儿?”

"gitee 的 openharmony 仓库 Release 页,或者 OpenHarmony 官网下载中心。"老陈说,“记得选 LTS 版本,别追 master。”

“为什么别追 master?”

"因为 master 是开发分支,天天在变,今天能编过明天可能就挂了。"老陈说,“LTS 是长期支持版,社区验证充分,配套的文档、教程、开发板支持都齐全。新手追新版本 = 给自己找罪受。”

姿势三:只拉某个芯片的样例(最快尝鲜)

“如果你只是想先看看长什么样,可以去 Gitee 找设备厂商提供的快速上手仓库,比如润和、小熊派这些厂商会打包好一套能直接编译的最小环境。这个最快,但学到的东西最少。”

四、编译:hb 是个什么玩意儿

"源码到手,开始编译。"老陈敲了敲键盘。

4.1 认识构建系统

“OpenHarmony 用的构建系统,是 GN + Ninja。”

┌──────────────────────────────────────────┐
│  hb(OpenHarmony 的构建命令行工具)          │  ← 你敲的是它
├──────────────────────────────────────────┤
│  GN(Generate Ninja)                      │  ← 生成构建描述文件
├──────────────────────────────────────────┤
│  Ninja                                    │  ← 真正执行编译
├──────────────────────────────────────────┤
│  LLVM / GCC 工具链                          │  ← 编译器
└──────────────────────────────────────────┘

“GN 是干嘛的?”

"GN 是一种’构建描述语言’。"老陈说,“你在每个目录里写的 BUILD.gn 文件,就是用 GN 语法描述:这个目录下有哪些源文件、要编成什么(可执行程序 / 静态库 / 动态库)、依赖谁。GN 读完全部 BUILD.gn,生成 build.ninja,然后 Ninja 按它执行编译。”

“听起来像 makefile?”

"就是 makefile 的现代替代品。"老陈说,“比 make 快得多,尤其是增量编译。”

他给我看了一个最简的 BUILD.gn:

# applications/sample/wifi-iot/app/my_led/BUILD.gn

static_library("my_led") {          # 目标:一个静态库,名叫 my_led
    sources = [                     # 源文件列表
        "my_led.c",
    ]

    include_dirs = [                # 头文件搜索路径
        "//utils/native/lite/include",
        "//base/iot_hardware/peripheral/interfaces/kits",
    ]

    deps = [                        # 依赖别的模块
        "//base/iot_hardware/peripheral:hal_iothardware",
    ]
}

“看懂了吗?”

“……大概。就是告诉编译器:编这个 .c,头文件去哪儿找,依赖谁。”

"够了。"老陈说,“GN 你不用精通,会照猫画虎改 BUILD.gn 就行。”

4.2 装 hb

# 在源码根目录执行
pip3 install --user build/lite      # 老版本路径
# 或者
pip3 install --user ohos-build      # 新版本可直接从 PyPI 装

# 加进 PATH(pip 装完会提示你加到 ~/.bashrc)
echo 'export PATH=~/.local/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

# 验证
hb --version

⚠️ 常见坑 1:装完 hb 提示 command not found。
原因:~/.local/bin 没进 PATH。
解法:手动加 export PATH=~/.local/bin:$PATH,然后 source ~/.bashrc。

4.3 三步编译

"编译就三步。"老陈伸出三根手指。

# 第一步:设置产品(选择要编译哪一款设备)
hb set
#    会弹出一个列表,用方向键选,比如 Hi3861 对应的产品
#    选完会在根目录生成 ohos_config.json
(示意:hb set 的输出)
Ohos Product List:
 1. hispark_pegasus@wifiiot_hispark_pegasus      ← Hi3861 选这个
 2. hispark_taurus@ipcamera_hispark_taurus       ← Hi3516
 3. rk3568@hihope_rk3568                          ← RK3568
Which would you like? [1]:
# 第二步:编译(-f 表示强制全量编译,第一次必须加)
hb build -f

# 第三步(可选):后续增量编译,去掉 -f
hb build

“-f 是什么?”

"force,全量重编。"老陈说,“第一次编译必须加,因为它要生成所有中间产物。后面你改了代码,直接 hb build 就行,它只会重编变动的部分——这就是 Ninja 的价值,全量要半小时,增量可能只要几秒。”

4.4 编译成功的样子

“成功的标志是最后几行长这样:”

[OHOS INFO] ...
[OHOS INFO] c targets overlap rate statistics
[OHOS INFO] ...
[OHOS INFO] wifiiot_hispark_pegasus build success
[OHOS INFO] cost time: 0:12:34

“看见 build success 就是成了。”

“产物在哪儿?”

ls out/hispark_pegasus/wifiiot_hispark_pegasus/

"找这个文件——"老陈指了指:

Hi3861_wifiiot_app_allinone.bin

“**这就是要烧进板子的固件。**名字里的 allinone 意思是’全部打包在一起’——内核、系统服务、你的应用,全在这一个 bin 里。”

“多大?”

"几百 KB 到一两 MB。"老陈说,“你对比一下安卓的系统镜像——几百 MB。这就是轻量系统的分量。”

五、烧录:把固件灌进板子

5.1 连线和串口

“烧录之前,先搞清楚两件事:串口和烧录模式。”

Hi3861 开发板
┌──────────────────────┐
│   [MicroUSB]  ← 供电 + 串口 + 烧录 三合一
│   [RST] 按键
│   [KEY] 按键(烧录时要用)
│   [蓝色 LED]
└──────────────────────┘

“一根 USB 线搞定供电、串口通信和烧录。”

“虚拟机怎么用 USB?”

"这是虚拟机的经典坑。"老陈说,“你插上 USB 线,虚拟机默认是看不见的,得手动把设备’透传’进去。”

VMware:   虚拟机 → 可移动设备 → 找到你的 USB-Serial 设备 → 连接
VirtualBox: 设备 → USB → 勾选对应的 USB 设备

⚠️ 常见坑 2:虚拟机里 ls /dev/ttyUSB* 什么都没有。
解法 1:确认 USB 设备已透传给虚拟机。
解法 2:Windows 主机上先确认能识别(设备管理器看端口),如果 Windows 占用了,虚拟机就拿不到。
解法 3:最省事的办法——烧录这一步直接在 Windows 上做,用 HiBurn 工具,串口调试再回 Ubuntu。
(我最后就是这么干的,省了两小时的折腾。)

5.2 用 HiBurn 烧录(Windows)

“HiBurn 是海思的串口烧录工具,Windows 下的,图形界面,傻瓜式。”

步骤:

1. 打开 HiBurn,选择串口号(COMx)
2. 波特率选 921600(默认最高档,烧得快)
3. 点 "Select file",选中 Hi3861_wifiiot_app_allinone.bin
4. 勾选 "Auto burn"
5. 点 "Connect"
6. 【关键】按下板子上的 RST 按键,让板子重启进入烧录模式
7. 进度条开始走,等它跑完
8. 显示 "Execution Successful" 即成功

“第六步是关键。“老陈强调,”**烧录不是插上线就能烧,你得让芯片进入 bootloader 的下载模式。**不同板子进入方式不同,Hi3861 是按 RST 重启后,bootloader 会短暂等待下载命令。”

“如果一直连不上呢?”

"按这个顺序排查——"老陈掏出一张纸:

症状 可能原因 解法
串口号都选不到 驱动没装 装 CH340 / CP210x USB 转串口驱动
连上了但一直等待 没进烧录模式 按 RST 键;或先按住 KEY 再按 RST
波特率太高,传一半失败 线材差 / 干扰 降到 115200 再试(慢但稳)
烧完了但板子没反应 没勾选 Auto burn / 文件选错 检查 bin 文件路径和选项
提示文件大于 Flash 编错产品了 确认 hb set 选的是 Hi3861 对应产品

5.3 串口看日志

“烧完,板子会自动重启。这时候打开串口工具,能看到启动日志——这是你以后调试的唯一眼睛。”

Ubuntu 下:

# 装 minicom
sudo apt install -y minicom

# 查看串口设备
ls /dev/ttyUSB*

# 打开(波特率 115200)
sudo minicom -D /dev/ttyUSB0 -b 115200

# 退出:Ctrl+A 然后按 X,选 Yes

⚠️ 常见坑 3:minicom 打不开,提示 Permission denied。
原因:当前用户不在 dialout 组。
解法:

sudo usermod -aG dialout $USER
# 然后注销重新登录(或者重启)

“启动日志长什么样?”

“你会看到一堆初始化信息刷屏,最后是类似这样的:”

...
[OHOS] LiteOS kernel init done
[OHOS] System service init ...
[OHOS] Wifi init success
[OHOS] app init ...
Hello, OpenHarmony!        ← 你的应用打印的

“看到你自己的日志,就说明系统跑起来了。”

六、翻车急救手册(这一节最值钱)

那一晚上我踩了七个坑。老陈说这些坑他见过几百个新手踩,让我原样记下来。

坑 1:磁盘爆满,编译到一半挂掉

症状:编译到 80% 报错,提示 "No space left on device"
原因:out 目录 + 源码 + prebuilts,100 G 只够勉强
解法:
  · 清理:hb clean 或删掉 out 目录重来
  · 扩容:虚拟机磁盘扩容(先关虚拟机,在设置里扩,再进系统用 gparted 扩分区)
  · 预防:一开始就给 150 G,别卡着底线给

坑 2:hb 报 Python 版本错

症状:SyntaxError / 各种 import 报错
原因:Python 版本过低(3.7 以下)或过高(3.12+ 部分脚本未适配)
解法:用 3.8 ~ 3.10,最稳

坑 3:prebuilts_download 卡住或失败

症状:下载工具链时超时 / 校验失败
原因:网络问题
解法:
  · 重试(这个脚本支持断点)
  · 手动下载对应工具链放到 prebuilts 目录
  · 换个时间段(晚上速度通常好一点)

坑 4:找不到头文件 / 链接失败

症状:fatal error: xxx.h: No such file or directory
原因:BUILD.gn 里的 include_dirs 或 deps 写漏了
解法:
  · 确认头文件实际路径(用 find 命令找)
  · 路径要用 "//" 开头表示源码根目录
  · 依赖的模块名去它自己的 BUILD.gn 里核对

坑 5:改了代码但编出来的固件没变化

症状:改了 .c 文件,重新烧录,行为没变
原因:忘了重新编译 / 编译的是别的产品 / 烧了旧的 bin
解法:
  · hb build(不带 -f 也应该能检测变化)
  · 实在不行:hb build -f 全量重编
  · 确认 ohos_config.json 里的产品是你要的那个
  · 确认烧录的 bin 路径是最新的(看文件时间)

坑 6:串口输出乱码

症状:minicom 里一堆乱码
原因:波特率不对
解法:确认是 115200(Hi3861 默认),8N1(8 数据位,无校验,1 停止位)

坑 7:虚拟机里编译极慢

症状:编译几小时不动
原因:CPU 核数给少了 / 磁盘 IO 差 / 内存不够导致 swap
解法:
  · CPU 给到 4 核以上
  · 内存 8G 起,别让系统用 swap
  · 虚拟机磁盘放在 SSD 上,别放机械盘
  · 用 hb build 的 -j 参数限制并发(内存不够时反而更快)

"最后一个建议——"老陈放下笔,“每一次成功,都把环境备份一份。”

“怎么备份?”

"虚拟机快照。"他说,“在你第一次 build success 的时候,立刻拍个快照。以后不管你怎么作死,一键回滚。这是你整个学习过程里,性价比最高的五分钟。”

七、北向预告:DevEco Studio

"南向的编译烧录你今晚过了一遍。"老陈看了眼时间,“最后十分钟,我给你看看另一半长什么样。”

他打开 Windows 上另一台机器,双击了一个图标。

“**DevEco Studio,北向应用开发的 IDE。**基于 IntelliJ IDEA 改的,你要是用过 Android Studio,界面会非常眼熟。”

他新建了一个项目,我看到了这样的目录结构:

MyApplication/
├── AppScope/                    ← 应用全局配置
│   ├── app.json5                ← 应用级别配置(包名、版本、图标)
│   └── resources/
├── entry/                       ← 默认模块(一个应用可以有多个模块)
│   ├── src/main/
│   │   ├── ets/                 ← 【ArkTS 代码都在这儿】
│   │   │   ├── entryability/
│   │   │   │   └── EntryAbility.ets    ← 应用入口
│   │   │   └── pages/
│   │   │       └── Index.ets           ← 页面
│   │   ├── resources/           ← 资源(字符串、图片、布局)
│   │   └── module.json5         ← 模块配置(Ability 声明、权限)
│   ├── build-profile.json5
│   └── oh-package.json5         ← 依赖管理(类似 package.json)
└── build-profile.json5

“几个关键文件记住:”

文件 作用 类比安卓
app.json5 应用级配置(Bundle 名、版本、图标) AndroidManifest.xml 的应用部分
module.json5 模块级配置(Ability、权限、设备类型) AndroidManifest.xml 的 Activity 部分
oh-package.json5 三方依赖管理 build.gradle 的依赖
EntryAbility.ets 应用入口 Ability MainActivity
pages/Index.ets 页面 UI activity_main.xml + Activity

"注意 json5 不是 json。"老陈提醒,“json5 允许注释、允许尾逗号,写起来舒服点。”

他点了一个按钮,右侧弹出了一个实时预览窗口,屏幕上显示着一个"Hello World"。

"这个叫预览器(Previewer),你写代码的时候它实时刷新,不用编译不用部署。"老陈说,“对新手极其友好——这一点比 Android Studio 做得好。”

“能直接在电脑上看效果?”

“能。还能开模拟器,跑一个完整的鸿蒙系统。”

我看着那个小小的预览窗口,忽然有点期待。

"好了,今天就到这儿。"老陈关掉 IDE,“今晚回去把环境装好,把源码拉下来,编一次,烧一次。哪怕只跑起来一个 Hello World,你也已经超过 80% 只看不练的人了。”

“那灯呢?”

老陈笑了。

“灯是下一章的事。”

=================小剧场模式=================

我:老师,我统计了一下,今晚我一共踩了七个坑。

老陈:七个算少的。 我第一次搭这套环境,踩了十几个,折腾了三天。

我:你是老手啊,也会踩?

老陈:环境搭建这件事,跟水平关系不大,跟运气关系大。 每个人机器不一样、网络不一样、系统版本不一样,踩的坑都不一样。

我:那新手遇到坑怎么办?

老陈:三步。第一,完整读一遍报错信息——90% 的报错里已经写了原因,只是你没看。

我:第二呢?

老陈:把报错原文复制去搜。 别自己造句子描述问题,就复制原文。你踩的坑,全网踩过的人成千上万。

我:第三?

老陈:看不懂就先跳过,别死磕。 有些坑是因为后面的步骤没做才出现的,继续往下走反而自己就消失了。

我:(记笔记)

老陈:还有最重要的一条——

我:什么?

老陈:第一次 build success 之后,立刻拍虚拟机快照。

========================================

八、本章速查卡

老陈临走前,在白板上留了这张表,让我贴在显示器边上:

我要干什么 命令 / 工具
选产品 hb set
全量编译 hb build -f
增量编译 hb build
清理产物 hb clean
下载工具链 bash build/prebuilts_download.sh
找固件 out/<board>/<product>/Hi3861_wifiiot_app_allinone.bin
烧录(Win) HiBurn,波特率 921600,按 RST 进下载模式
看日志(Ubuntu) sudo minicom -D /dev/ttyUSB0 -b 115200
串口没权限 sudo usermod -aG dialout $USER 后重登录
北向 IDE DevEco Studio(写 ArkTS)
南向 IDE DevEco Device Tool(VSCode 插件,编固件)
构建系统 GN(描述) + Ninja(执行),hb 是入口

"最后一句。"老陈在门口转身。

“今晚不管编出来什么,烧进去,看串口日志,确认它真的在跑。”

“为什么强调这个?”

“因为在嵌入式这行,'编译通过’和’真的能跑’之间,隔着一整个世界。”


本章自检

过一遍下面的问题。答不上来的,回去翻对应小节——那些就是你这个章的欠账。

  • ☐ hb set 和 hb build 分别做什么?
  • ☐ 编译产物在哪个目录?文件名是什么?
  • ☐ 烧录时为什么要按 RST?连不上该查什么?
  • ☐ 串口日志打不出来,按什么顺序排查?
  • ☐ 最重要:第一次编译成功后,你拍虚拟机快照了吗?

**➡️ 下一站:**第 04 章 · 点灯 —— 终于可以让硬件动起来了。


=================剧情片段=================

导演:卡。这一章是不是有点"工具说明书"的味道?

助理:确实,命令多、故事少。

导演:这一章本来就该是工具书。 观众是照着它一步步操作的,这时候花活没意义,准确和齐全才重要。

助理:但会不会劝退?

导演:会。 这就是现实——环境搭建就是劝退率最高的地方。我们能做的,是把坑提前告诉他,让他少摔几次。

助理:下一章应该轻松多了吧?

导演:下一章才是真正的开始。

助理:《点灯》。

导演:对。GPIO、HDF 驱动框架、hcs 配置、驱动模型——从"让一个引脚变高"到"让一个操作系统去管理一个引脚"。

助理:听起来还是点灯啊。

导演:是点灯。但这次点灯,他要写的是一个驱动。 前者是操作寄存器,后者是给系统写一个"能被人调用的能力"。

助理:懂了。同一个动作,两种境界。

导演:这就是南向开发的入门礼。

========================================

未完待续。。。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

评论

欢迎来到我的博客!