普通视图

发现新文章,点击刷新页面。
昨天以前东方星痕

仓颉语言 Neovim 开发环境配置指南

作者 YSTYLE
2026年4月3日 23:30

本文档介绍如何在 openEuler (aarch64) 系统上配置 Neovim (AstroNvim) 开发环境,用于仓颉语言开发。

目录

系统要求

  • 操作系统:openEuler 2403 SP1 (aarch64)
  • GCC 版本:12.3.1 或更高
  • Neovim 版本:0.9.0 或更高

安装仓颉 SDK

  1. 从官方渠道下载仓颉 SDK (aarch64 版本)
  2. 解压到 ~/.config/cjvs/store/ 目录

目录结构示例:

1
2
3
4
5
6
7
~/.config/cjvs/
└── store/
└── 1.1.0-beta.25/
├── envsetup.sh
├── lib/
├── runtime/
└── ...

安装 Cangjie STDX

STDX 是仓颉的标准扩展库,需要单独安装:

  1. 从发布页面下载:https://gitcode.com/Cangjie/cangjie_stdx/releases/v1.1.0-beta.24.1
  2. 选择 linux_aarch64_cjnative 版本
  3. 解压到 ~/.config/cjvs/stdx/ 目录

目录结构示例:

1
2
3
4
5
6
7
8
~/.config/cjvs/
└── stdx/
└── 1.1.0-beta.24/
└── linux_aarch64_cjnative/
└── static/
└── stdx/
├── stdx.cjo
└── ...

环境变量配置

编辑 ~/.zshrc,添加以下配置:

1
2
3
4
5
# 仓颉 SDK 环境配置
source $HOME/.config/cjvs/store/1.1.0-beta.25/envsetup.sh

# STDX 路径配置
export CANGJIE_STDX_PATH="$HOME/.config/cjvs/stdx/1.1.0-beta.24/linux_aarch64_cjnative/static/stdx"

配置完成后执行:

1
source ~/.zshrc

说明

  • envsetup.sh:设置仓颉编译器所需的环境变量
  • CANGJIE_STDX_PATH:指定 STDX 库的路径

创建 GCC 运行时库符号链接

由于仓颉链接器脚本默认搜索 /lib64 目录,需要创建符号链接:

1
2
3
4
sudo ln -sf /usr/lib/gcc/aarch64-openEuler-linux/12/crtbeginS.o /lib64/crtbeginS.o
sudo ln -sf /usr/lib/gcc/aarch64-openEuler-linux/12/crtendS.o /lib64/crtendS.o
sudo ln -sf /usr/lib/gcc/aarch64-openEuler-linux/12/crtbegin.o /lib64/crtbegin.o
sudo ln -sf /usr/lib/gcc/aarch64-openEuler-linux/12/crtend.o /lib64/crtend.o

说明:解决链接时找不到 crtbeginS.o 等运行时库的问题

Neovim 配置

1. 安装 AstroNvim

1
git clone --depth 1 https://github.com/AstroNvim/AstroNvim ~/.config/nvim

2. 配置仓颉 LSP 插件

创建文件 ~/.config/nvim/lua/plugins/cangjie-lsp.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
return {
"https://gitcode.com/ystyle/cangjie-nvim",
dependencies = { "neovim/nvim-lspconfig" },
opts = { auto_install = true },
config = function(plugin)
require("cangjie-nvim").setup(plugin.opts)

vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
if vim.bo[args.buf].filetype ~= "Cangjie" then return end

local function map(mode, lhs, rhs, desc)
vim.keymap.set(mode, lhs, rhs, { buffer = args.buf, desc = desc })
end

map("n", "<leader>li", "<cmd>LspInfo<cr>", "LSP Info")
map("n", "K", vim.lsp.buf.hover, "Hover Document")
map("n", "<leader>lf", function() vim.lsp.buf.format { async = true } end, "Format Document")
map("n", "gl", vim.diagnostic.open_float, "Line Diagnostics")
map("n", "<leader>ld", vim.diagnostic.open_float, "Line Diagnostics")
map("n", "<leader>lD", vim.diagnostic.setqflist, "All Diagnostics")
map("n", "gra", vim.lsp.buf.code_action, "Code Actions")
map("n", "<leader>la", vim.lsp.buf.code_action, "Code Actions")
map("n", "<leader>lh", vim.lsp.buf.signature_help, "Signature Help")
map("n", "grn", vim.lsp.buf.rename, "Rename")
map("n", "<leader>lr", vim.lsp.buf.rename, "Rename")
map("n", "<leader>ls", vim.lsp.buf.document_symbol, "Document Symbols")
map("n", "<leader>lG", vim.lsp.buf.workspace_symbol, "Workspace Symbols")
map("n", "]d", vim.diagnostic.goto_next, "Diagnostic Next")
map("n", "[d", vim.diagnostic.goto_prev, "Diagnostics Previous")
map("n", "]e", function() vim.diagnostic.goto_next { severity = vim.diagnostic.severity.ERROR } end, "Error Next")
map("n", "[e", function() vim.diagnostic.goto_prev { severity = vim.diagnostic.severity.ERROR } end, "Error Previous")
map("n", "]w", function() vim.diagnostic.goto_next { severity = vim.diagnostic.severity.WARN } end, "Warning Next")
map("n", "[w", function() vim.diagnostic.goto_prev { severity = vim.diagnostic.severity.WARN } end, "Warning Previous")
map("n", "gD", vim.lsp.buf.declaration, "Declaration")
map("n", "gy", vim.lsp.buf.type_definition, "Type Definition")
map("n", "gd", vim.lsp.buf.definition, "Definition")
map("n", "gri", vim.lsp.buf.implementation, "Implementation")
map("n", "grr", vim.lsp.buf.references, "References")
end,
})
end,
}

3. 启动 Neovim 并安装插件

1
nvim

AstroNvim 会自动检测并安装 cangjie-nvim 插件。首次打开 .cj 文件时,插件会自动下载 LSP wrapper 二进制文件。

LSP Wrapper 地址

常见问题

1. 链接错误:cannot find crtbeginS.o

错误信息

1
/usr/bin/ld: cannot find crtbeginS.o: No such file or directory

解决方案
创建 GCC 运行时库符号链接:

1
2
3
4
sudo ln -sf /usr/lib/gcc/aarch64-openEuler-linux/12/crtbeginS.o /lib64/crtbeginS.o
sudo ln -sf /usr/lib/gcc/aarch64-openEuler-linux/12/crtendS.o /lib64/crtendS.o
sudo ln -sf /usr/lib/gcc/aarch64-openEuler-linux/12/crtbegin.o /lib64/crtbegin.o
sudo ln -sf /usr/lib/gcc/aarch64-openEuler-linux/12/crtend.o /lib64/crtend.o

2. LSP 不工作

检查步骤

  1. 确认仓颉 SDK 环境变量已加载:echo $CANGJIE_HOME
  2. 确认 LSP wrapper 已下载:ls ~/.local/share/nvim/data/cangjie-nvim/bin/
  3. 手动安装 LSP wrapper:
    1
    :lua require("cangjie-nvim").install()

3. 找不到 STDX

错误信息

1
error: cannot find stdx module

解决方案
确认 CANGJIE_STDX_PATH 环境变量指向正确的目录:

1
2
ls $CANGJIE_STDX_PATH
# 应该能看到 stdx.cjo 文件

4. 快速事件上下文错误

错误信息

1
E5560: nvim_create_namespace must not be called in a fast event context

解决方案
此问题已在 cangjie-nvim 插件中修复,请确保使用最新版本。

键位映射

快捷键功能
K显示悬停文档
gd跳转到定义
gD跳转到声明
gy跳转到类型定义
gri跳转到实现
grr查找引用
grn重命名
<leader>lf格式化文档
<leader>la代码操作
<leader>liLSP 信息
]d / [d下一个/上一个诊断
]e / [e下一个/上一个错误
]w / [w下一个/上一个警告

参考链接

鸿蒙PC融合开发引擎中编译安装Neovim与AstroNvim配置指南

作者 YSTYLE
2026年4月3日 21:35

鸿蒙PC融合开发引擎中编译安装Neovim与AstroNvim配置指南

本文记录在鸿蒙PC融合开发引擎的容器环境中,因glibc版本限制从源码编译Neovim和tree-sitter的完整过程,并配置AstroNvim作为开发环境。

问题背景

glibc版本冲突

在鸿蒙PC融合开发引擎的openEuler容器中,尝试安装nvim-treesitter插件时遇到错误:

1
2
[nvim-treesitter/install/c] error: Error during "tree-sitter build": 
tree-sitter: /usr/lib64/libc.so.6: version `GLIBC_2.39' not found (required by tree-sitter)

版本检查

1
2
3
4
5
6
ldd --version
# 输出:ldd (GNU libc) 2.38

cat /etc/os-release
# NAME="openEuler"
# VERSION="24.03 (LTS-SP1)"

openEuler 24.03 SP1的glibc是2.38,而nvim-treesitter下载的预编译tree-sitter CLI需要glibc 2.39。

根本原因

容器环境共享虚拟机内核,无法升级glibc。解决方案是从源码编译所有组件,使用当前系统的glibc版本。

解决方案总览

1
2
3
4
5
1. 安装编译依赖
2. 安装Rust工具链(编译tree-sitter CLI需要)
3. 从源码编译tree-sitter CLI
4. 从源码编译Neovim
5. 配置AstroNvim

一、安装编译依赖

基础工具

1
2
3
4
5
6
# 安装cmake和ninja
sudo dnf install -y cmake ninja-build

# 验证安装
cmake --version
ninja --version

其他依赖

1
2
3
# gcc、make等已预装
gcc --version
make --version

二、安装Rust工具链

tree-sitter CLI使用Rust编写,需要先安装Rust:

1
2
3
4
5
6
7
8
9
10
11
12
# 设置代理(可选)
export https_proxy=http://192.168.3.6:1080

# 安装Rust
curl --proto '=https' -sSf https://sh.rustup.rs | sh -s -- -y

# 加载环境
source ~/.cargo/env

# 验证
cargo --version
rustc --version

三、编译tree-sitter CLI

获取源码

1
2
3
4
5
6
export https_proxy=http://192.168.3.6:1080

# 克隆tree-sitter CLI(使用稳定版本v0.24.1)
git clone --depth 1 --branch v0.24.1 \
https://github.com/tree-sitter/tree-sitter.git \
~/tree-sitter-cli

编译

1
2
cd ~/tree-sitter-cli
cargo build --release

编译过程约需1-2分钟。

安装

1
2
3
4
5
6
7
8
9
10
# 系统安装
sudo cp ~/tree-sitter-cli/target/release/tree-sitter /usr/local/bin/

# 或替换mason的预编译版本
cp ~/tree-sitter-cli/target/release/tree-sitter \
~/.local/share/nvim/mason/packages/tree-sitter-cli/tree-sitter-linux-arm64

# 验证
tree-sitter --version
# 输出:tree-sitter 0.24.1

四、编译Neovim

获取源码

1
2
3
4
5
6
7
8
9
10
export https_proxy=http://192.168.3.6:1080

# 查看最新版本
git ls-remote --tags https://github.com/neovim/neovim.git | \
grep -v '\^{}' | grep 'v0\.1[0-2]' | tail -5

# 克隆最新稳定版v0.12.0
git clone --depth 1 --branch v0.12.0 \
https://github.com/neovim/neovim.git \
~/neovim

编译

1
2
cd ~/neovim
make CMAKE_BUILD_TYPE=Release

编译过程约需3-5分钟,会自动下载并编译所有依赖。

安装

1
2
3
4
5
6
7
8
sudo make install

# 验证
nvim --version
# 输出:
# NVIM v0.12.0
# Build type: Release
# LuaJIT 2.1.1774638290

编译输出位置

1
2
ls ~/neovim/build/bin/nvim
# -rwxr-xr-x 1 user user 10346568 ... nvim

五、配置AstroNvim

AstroNvim是一个预配置的Neovim发行版,提供完整的IDE体验。

备份现有配置

1
2
3
4
# 备份旧配置
mv ~/.config/nvim ~/.config/nvim.bak
mv ~/.local/share/nvim ~/.local/share/nvim.bak
mv ~/.local/state/nvim ~/.local/state/nvim.bak

安装AstroNvim

1
2
3
4
5
6
# 克隆AstroNvim配置
git clone --depth 1 https://github.com/AstroNvim/AstroNvim \
~/.config/nvim

# 复制默认用户配置模板
cp ~/.config/nvim/lua/user_template.lua ~/.config/nvim/lua/user.lua

首次启动

1
nvim

首次启动会自动安装所有插件和tree-sitter解析器。由于我们已编译了tree-sitter CLI,treesitter安装不会出现glibc错误。

自定义配置

编辑 ~/.config/nvim/lua/user.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
return {
-- 自定义颜色方案
colorscheme = "catppuccin-mocha",

-- 自定义选项
options = {
opt = {
relativenumber = true, -- 相对行号
},
},

-- 自定义插件
plugins = {
"rebelot/kanagawa.nvim",
-- 添加更多插件...
},

-- LSP配置
lsp = {
formatting = {
format_on_save = true,
},
servers = {
-- 添加LSP服务器...
},
},
}

安装LSP和工具

在Neovim中运行:

1
2
3
4
5
:LspInstall python
:LspInstall lua_ls
:TSInstall python
:TSInstall lua
:MasonInstall prettier

六、验证tree-sitter工作

安装解析器

1
:TSInstallInfo

查看可用的解析器,选择需要的安装:

1
2
3
4
5
6
7
8
:TSInstall vimdoc
:TSInstall lua
:TSInstall python
:TSInstall javascript
:TSInstall html
:TSInstall css
:TSInstall json
:TSInstall markdown

检查状态

1
:TSInstallInfo

所有解析器应显示为 ✓ installed

七、总结

编译清单

组件版本状态
cmake3.27.9✓ 已安装
ninja1.11.1✓ 已安装
Rust1.94.1✓ 已安装
tree-sitter CLI0.24.1✓ 已编译
Neovim0.12.0✓ 已编译
AstroNvimlatest✓ 已配置

关键点

  1. glibc兼容:从源码编译确保使用当前glibc
  2. Rust依赖:tree-sitter CLI需要Rust编译
  3. 完整流程:依赖 → Rust → tree-sitter → Neovim → AstroNvim

性能表现

编译的Neovim在openEuler容器中运行流畅:

  • 启动速度:< 100ms
  • LSP响应:快速
  • Treesitter高亮:正常
  • 插件生态:完整支持

避坑指南

  1. 不要使用预编译tree-sitter:mason下载的预编译版本glibc不兼容
  2. 记得替换mason的tree-sitter:否则nvim-treesitter仍会使用预编译版本
  3. 使用稳定版本:避免main分支的不稳定性

八、清理构建文件

编译完成后可以清理源码:

1
2
3
4
# 可选:保留源码用于后续升级
# 或删除以节省空间
rm -rf ~/tree-sitter-cli
rm -rf ~/neovim

建议保留源码目录,方便后续版本升级。

九、后续升级

升级Neovim:

1
2
3
4
5
cd ~/neovim
git fetch --tags
git checkout v0.13.0 # 假设新版本
make CMAKE_BUILD_TYPE=Release
sudo make install

升级tree-sitter:

1
2
3
4
5
cd ~/tree-sitter-cli
git fetch --tags
git checkout v0.25.0 # 假设新版本
cargo build --release
sudo cp target/release/tree-sitter /usr/local/bin/

参考资源

鸿蒙PC融合开发引擎架构解析:虚拟机与容器双模式

作者 YSTYLE
2026年4月3日 21:30

鸿蒙PC融合开发引擎架构解析:虚拟机与容器双模式

本文深入解析鸿蒙PC融合开发引擎的技术架构,揭示其如何通过StratoVirt虚拟化和OzoneC容器技术实现Linux应用兼容。

背景

鸿蒙PC(HarmonyOS PC)采用了独特的架构设计来实现Linux应用兼容。作为用户,我在使用过程中发现系统提供了一个”融合开发引擎”功能,可以在其中运行openEuler环境。本文将解析这个融合开发引擎的技术架构。

架构探秘

初次接触

在鸿蒙PC的融合开发引擎环境中,我尝试运行各种Linux工具时发现:

  1. 文件系统特征:使用overlay文件系统,路径包含/var/lib/OzoneC/overlay2/
  2. 进程信息:cgroup显示典型的容器特征
  3. 内核版本:Linux 6.6.0,带有特殊的init进程

通过一系列探测,我逐渐揭示了其架构。

架构层次

经过分析,鸿蒙PC融合开发引擎采用的是三层架构

1
2
3
4
5
6
7
鸿蒙PC(HarmonyOS 微内核)

StratoVirt 虚拟机(运行Linux)

OzoneC 容器(隔离Linux应用环境)

用户环境(openEuler容器)

关键证据

1. 虚拟机特征

1
2
3
4
5
6
7
8
9
10
# 查看固件信息
ls -la /sys/firmware/
# 输出包含:qemu_fw_cfg - 这是QEMU/StratoVirt虚拟机的特征

# 查看内核启动参数
cat /proc/cmdline
# 输出:console=hvc0 root=/dev/vda ro reboot=k panic=1 init=/usr/sbin/HSLd
# - hvc0是virtio虚拟机控制台
# - /dev/vda是虚拟块设备
# - init=/usr/sbin/HSLd是HarmonyOS Linux兼容层的初始化程序

2. 容器特征

1
2
3
4
5
6
7
8
9
10
11
12
# 查看挂载信息
cat /proc/mounts
# 输出:
overlay / overlay rw,dirsync,nodev,relatime,
lowerdir=/var/lib/OzoneC/overlay2/rgm_openEuler/lower,
upperdir=/var/lib/ozonec/bundle/openeuler2203fgjM0CXQxp5yLHfFZOK/diff,
workdir=/var/lib/ozonec/bundle/openeuler2203fgjM0CXQxp5yLHfFZOK/work

# 这表明:
# - 使用overlay2存储驱动
# - OzoneC是容器运行时
# - openEuler是容器镜像

3. 内核分析

1
2
3
4
5
6
7
8
9
# 查看内核版本
uname -a
# Linux localhost 6.6.0 aarch64

# 查看CPU信息
cat /proc/cpuinfo
# CPU implementer: 0x41 (ARM)
# CPU architecture: 8
# 没有hypervisor标志,因为运行在虚拟机中

StratoVirt:企业级虚拟化平台

项目介绍

StratoVirt是计算产业中面向云数据中心的企业级虚拟化平台,实现了一套架构统一支持虚拟机、容器、Serverless三种场景。在鸿蒙PC上,它作为Linux兼容的基础设施。

关键特性

  1. 轻量低噪:高效的虚拟化实现
  2. 软硬协同:充分利用硬件特性
  3. Rust语言级安全:内存安全的实现
  4. 架构统一:支持多种场景

运行模式

StratoVirt支持两种机型:

microvm机型(轻量级):

1
2
3
4
5
6
7
./stratovirt \
-machine microvm \
-kernel /path/to/kernel \
-append "console=ttyAMA0 root=/dev/vda" \
-drive file=/path/to/rootfs,id=rootfs \
-device virtio-blk-device,drive=rootfs \
-serial stdio

标准机型(完整功能):

1
2
3
4
5
6
./stratovirt \
-machine virt \
-kernel /path/to/kernel \
-drive file=/path/to/firmware,if=pflash \
-device virtio-blk-pci,drive=rootfs \
-serial stdio

OzoneC:容器运行时

项目定位

OzoneC是StratoVirt项目内置的OCI容器运行时,使用Rust语言实现。从源码和挂载路径/var/lib/OzoneC/overlay2/可以确认其使用。

技术特点

OzoneC是一个完整的OCI runtime实现:

  • 开发团队:Huawei StratoVirt Team
  • 实现语言:Rust(内存安全)
  • 标准兼容:完全符合OCI runtime-spec
  • 支持命令:create, start, state, kill, delete, exec

核心功能

1
2
3
4
5
6
7
8
9
# OCI标准命令
ozonec create <container-id> # 创建容器
ozonec start <container-id> # 启动容器
ozonec state <container-id> # 查询状态
ozonec kill <container-id> # 终止容器
ozonec delete <container-id> # 删除容器

# 扩展命令
ozonec exec <container-id> # 在容器中执行命令

架构优势

  1. Rust实现:内存安全、无数据竞争
  2. 轻量级:专为虚拟机场景优化
  3. 原生集成:与StratoVirt紧密配合
  4. OCI兼容:支持标准容器镜像

与StratoVirt的协作

OzoneC在StratoVirt虚拟机内部运行,形成双重隔离:

  1. 虚拟机级隔离:StratoVirt提供硬件级隔离
  2. 容器级隔离:OzoneC提供应用级隔离

这种设计兼顾了安全性和性能,且OzoneC作为StratoVirt的子项目,实现了无缝集成。

融合开发引擎的工作原理

启动流程

  1. 鸿蒙PC启动:微内核系统初始化
  2. StratoVirt启动:创建Linux虚拟机
  3. HSLd初始化:HarmonyOS Linux Layer daemon启动
  4. OzoneC运行:创建openEuler容器
  5. 用户环境:提供Shell和开发工具

Linux兼容层(HSLd)

init=/usr/sbin/HSLd参数表明使用了HarmonyOS Linux Layer daemon:

  • 负责Linux系统服务的初始化
  • 提供鸿蒙与Linux的交互接口
  • 管理Linux应用的生命周期

容器环境

openEuler容器提供:

  • 完整的Linux用户空间
  • RPM包管理(dnf/yum)
  • 开发工具链(gcc、make等)
  • 运行时环境(Python、Node.js等)

技术优势

1. 安全隔离

  • 虚拟机隔离防止Linux应用影响鸿蒙系统
  • 容器隔离防止不同Linux应用相互影响
  • 多层防御,安全可靠

2. 性能优化

  • StratoVirt使用Rust实现,内存安全且高效
  • virtio设备提供高性能I/O
  • overlay文件系统减少存储开销

3. 灵活部署

  • 支持不同Linux发行版容器
  • 可以定制开发环境
  • 快速部署和销毁

实际应用

开发环境

在融合开发引擎中,开发者可以:

  • 使用完整的Linux开发工具链
  • 运行服务器软件(nginx、redis等)
  • 编译Linux应用程序
  • 使用容器技术部署应用

使用限制

由于容器共享宿主机内核:

  • 无法更换内核版本:虚拟机内核固定
  • glibc版本受限:容器使用虚拟机的glibc
  • 内核模块不可加载:无内核权限

解决方案

对于glibc版本问题,可以:

  1. 从源码编译:使用当前glibc编译应用
  2. 使用静态链接:避免依赖系统库
  3. 使用隔离环境:conda、nix等提供独立环境

与传统方案的对比

对比WSL

特性鸿蒙融合开发引擎WSL2
架构虚拟机+容器虻拟机
内核Linux 6.6Linux 5.x
容器支持OzoneCDocker
镜像管理鸿蒙生态Docker生态
安全隔离双层隔离单层隔离

对比传统虚拟机

特性StratoVirtQEMU/KVM
实现语言RustC
内存安全语言级需要编码规范
启动速度快速传统
容器集成OzoneC需额外配置

总结

鸿蒙PC融合开发引擎通过StratoVirt虚拟化和OzoneC容器技术,巧妙地实现了Linux应用兼容:

  1. 微内核架构:鸿蒙保持自身特性
  2. 虚拟化隔离:StratoVirt运行Linux
  3. 容器管理:OzoneC提供应用环境
  4. 双层隔离:安全性和性能兼顾

这种架构设计体现了鸿蒙生态的创新思路,既保持了鸿蒙的微内核优势,又通过虚拟化和容器技术实现了Linux兼容,为开发者提供了熟悉的工作环境。


参考资料

MCP-Term:一个未出生即夭折的想法

作者 YSTYLE
2026年3月19日 20:00

⚠️ 已弃坑

此项目在讨论阶段即放弃,无任何代码实现。
放弃原因:市场风向转变,目标场景已被钉钉、Obsidian CLI, anything CLI 等原生工具覆盖。
记录此文仅为保存思考过程。


起因:MCP 协议的 Token 困境

一切始于一个问题:MCP 和 Function Call 有什么区别?

讨论后得出结论:

  • Function Call 是模型的底层能力
  • MCP 是应用层协议,解决生态碎片化
  • 但 MCP 的 JSON Schema 在 LLM 上下文中极度费 tokens

一个复杂工具的 JSON Schema 定义可能消耗 500-1000 tokens,而模型能理解更紧凑的格式——比如 TypeScript 的 .d.ts

核心想法:用 S-Expr 替代 JSON

方案演进

最初设想:修改 MCP 协议

  • Schema 用 .d.ts 定义(省 50% tokens)
  • 传输用 CBOR(省带宽)

第一次收缩:不改协议,做转换层

  • 客户端转换:JSON Schema → S-Expr → LLM
  • 调用时:LLM 生成 S-Expr → 转回 JSON-RPC
  • 兼容现有 MCP Server,零改动

最终定位:CLI 工具

  • 不是协议扩展,而是命令行包装器
  • 解决复杂嵌套参数在 CLI 中的表示问题

MCP-Term 协议设计

设计了名为 MCP-Term(原名 MCP-Symbol)的 S-Expr 格式。

基础语法

1
2
3
4
5
6
; 原子类型
number := [0-9]+(\.[0-9]+)? ; 整数或浮点
string := "([^"\\]|\\.)*" ; 双引号字符串
symbol := [a-zA-Z_][a-zA-Z0-9_]* ; 标识符
keyword := :[a-zA-Z_][a-zA-Z0-9_]* ; 冒号前缀,如 :utf8
list := '(' expr* ')' ; S-表达式列表

工具声明

1
2
3
4
5
6
7
8
9
10
(declare
(symbol long-name params return-type doc-string?)
...
)

; symbol : 短码,1-4 字符,调用时用
; long-name : 人类可读全称
; params : (param-name type default? option*)*
; return-type : 返回类型
; doc-string : 可选描述

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(declare
;; 文件操作
(fr file_read
(path string)
(opt (enc (or :utf8 :base64) :utf8)
(off (opt number) nil)
(lim (opt number) nil))
string
"Read file content")

;; 笔记操作
(nu note_update
(doc string)
(ops (list Op))
(tuple (ver number) (blocks (list Block)))
"Update document with operations")
)

类型系统

基础类型string number bool bytes any

复合类型

1
2
3
(list type)           ; 同质列表 => string
(map key-type val-type) ; 映射 => int
(tuple type*) ; 异构元组 => int) ; 联合类型 => :utf8 :base64) ; 可选,等价 => nil

自定义类型

1
2
3
4
5
6
7
8
9
10
(deftype Op
(i insert (pos number) (blocks (list Block)))
(d delete (pos number) (len number))
(r retain (pos number)))

(deftype Block
(p paragraph (id string) (props (map keyword any)) (content (list Inline)))
(h heading (level (or 1 2 3 4 5 6)) (content (list Inline)))
(c code (lang (opt string)) (content string))
(i image (src string) (alt string) (props (map keyword any))))

调用格式

1
2
3
4
5
6
7
8
9
10
11
12
13
; 简单调用
(fr "/etc/hosts" :utf8)

; 嵌套调用(笔记编辑)
(nu "doc.md"
(i 0 ((p "intro" () ("Hello world"))))
(d 20 5))

; 批量调用
(batch
(1 (fr "/etc/passwd"))
(2 (fr "/etc/hosts"))
(3 (ex "whoami")))

返回格式

1
2
3
4
5
6
7
; 成功
(ok "file content here")
(ok (out "root" code 0))

; 错误
(err 404 "file not found")
(err 1 "command failed" (stderr "permission denied"))

JSON Schema 映射

JSON SchemaMCP-Term
{"type": "string"}string
{"type": "number"}number
{"type": "boolean"}bool
{"type": "array"}(list any)
{"type": "object"}(map keyword any)
{"enum": ["a","b"]}(or :a :b)
{"type": ["string","null"]}(opt string)
{"properties": {...}}maptuple

复杂示例:企业数据查询

JSON Schema(约 3500 字符,1000+ tokens):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "data_platform_query",
"inputSchema": {
"type": "object",
"properties": {
"sources": {
"type": "array",
"items": {
"oneOf": [
{"type": "object", "properties": {"type": {"const": "sql"}, ...}},
{"type": "object", "properties": {"type": {"const": "mongodb"}, ...}},
{"type": "object", "properties": {"type": {"const": "api"}, ...}}
]
}
},
"joins": {...},
"transformations": {...},
"output": {...}
}
}
}

MCP-Term(约 600 字符,170 tokens):

1
2
3
4
5
6
7
8
9
10
11
12
(dp data_platform_query
(qid string)
(srcs (list Source 1 10))
(joins (opt (list Join)))
(trans (opt (list Xform)))
(out Output)
-> QueryResult)

(deftype Source (or SQL Mongo API))
(deftype SQL (sql (driver :pg|:my|:ch) (host string) (db string) (q (sql string))))
(deftype Join (join (left string) (right string) (type :inner|:left|:right) (on (list Cond))))
(deftype Xform (or (filter (cond string)) (agg (group (list string)) (aggs map))))

Token 节省对比

场景JSON SchemaMCP-Term节省
简单工具(3参数)1806067%
复杂工具(10参数)60015075%
超复杂(嵌套查询)350060083%
实际调用(嵌套操作)2005075%

市场验证:风向已变

现有方案调研

调研了几个 MCP-CLI 项目:

  1. mcp-cli:直接传 JSON 字符串,绕开参数问题
  2. FastMCP:简单 key=value 参数
  3. cyclopts:自动生成 --flag,嵌套时参数爆炸

关键发现:都没解决深层嵌套,但用户似乎不在意——因为有更简单的方案。

场景消失

原计划的目标场景:

场景1:CLI 工具嵌套参数

  • 但市场风向已变:钉钉 CLI、Obsidian CLI、香港大学的 anything CLI 等纷纷涌现
  • CLI + Skills 模式成为大趋势:用户通过 skills 模板封装复杂操作,而非手写嵌套参数
  • 管道 + base64 等简单方案已覆盖大部分场景
  • 结论:无需 S-Expr,市场已给出更简单的答案

场景2:数据复杂查询

  • 现有方案:BI 工具(可视化)、JSON 配置
  • 结论:市场 niche,且竞品成熟

市场变化

  • 原生 CLI 工具链成熟,各家都在做自己的 CLI
  • Skills 模板封装成为主流,复杂操作被预定义
  • 大模型直接操作文件/API,中间层价值稀释

放弃决策

直接原因

  1. 场景消失:复杂嵌套参数需求被简化方案覆盖
  2. 竞争出现:原生 CLI 生态成熟,无差异化空间
  3. 时间成本:继续验证已无意义,市场已给出答案

根本原因

  • 从”协议扩展”退到”CLI 工具”时,价值已打折
  • CLI 层无技术壁垒,易被复制/绕过
  • 大模型进化快,中间层易被端到端替代

收获

虽无代码产出,但获得:

  1. 快速验证方法论:市场调研 → 场景分析 → 放弃决策
  2. 止损判断标准:当目标场景被简化方案覆盖时,放弃
  3. MCP 生态理解:协议设计、现有工具链格局

未来信号

若出现以下情况,可重新评估:

  1. 新协议需要紧凑嵌套表示
  2. LLM 上下文窗口瓶颈重现(<4K)
  3. 特定领域出现复杂结构 CLI 痛点

当前:无。

鸿蒙PC Matebook Pro上全局安装GIT

作者 YSTYLE
2025年12月28日 10:30

准备工作

  • 在鸿蒙应用商店安装 CodeArts IDE

从CodeArts IDE提取签名工具

打开IDE, 随便创建一个项目打开,在终端执行以下命令

1
2
3
4
5
6
7
8
9
10
11
12
# 复制签名工具
cp /data/app/toolchains.org/toolchains_1.0/lib//binary-sign-tool .
# 对签名工具签名
binary-sign-tool sign -inFile "./binary-sign-tool" -outFile "./binary-sign-tool-signed" -selfSign 1
# 添加可执行权限
chmod +x binary-sign-tool-signed
# 测试,如果没弹授权弹窗和帮忙输出,需要重新签名和添加可执行权限
./binary-sign-tool-signed

# 复制到用户安装目录
mkdir -p ~/.local/bin
cp ./binary-sign-tool-signed ~/.local/bin/binary-sign-tool

最后在~/.zshrc添加export PATH=$HOME/.local/bin:$PATH

保存签名脚本

把以下保存为~/.local/bin/hm-sign-all.sh, 并执行chmod +x ~/.local/bin/hm-sign-all.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/sh

# 递归查询当前目录及其子目录下的所有文件
find . -type f | while read -r file; do
# 使用 file 命令检查文件类型
if file "$file" | grep "shared object"; then
echo "Found executable or shared object: $file"

# 确保文件具有可执行权限
chmod +x "$file"

# 签名文件
echo "Signing file: $file"
binary-sign-tool sign -inFile "$file" -outFile "$file" -selfSign 1
fi
done

从CodeArts IDE提取git

打开IDE, 随便创建一个项目打开,在终端执行以下命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建安装目录
mkdir -p ~/.local/opt

# 切换到安装目录, 并复制git
cd ~/.local/opt
cp -r /data/app/git.org/git_1.2/ .

# 对git可执行文件签名
hm-sign-all.sh

# 创建bin的软件链接
ln -s $HOME/.local/opt/git_1.2/bin/git $HOME/.local/bin/
ln -s $HOME/Users/currentUser/.local/opt/git_1.2/bin/git-lfs $HOME/.local/bin/

配置git环境

~/.zshrc添加以下内容

1
2
3
# git
export GIT_EXEC_PATH=$HOME/.local/opt/git_1.2/libexec/git-core
export GIT_TEMPLATE_DIR=$HOME/.local/opt/git_1.2/share/git-core/templates

测试

可以看到,可以正常clone仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
➜ cd Documents/DevEcoStudioProjects ✓
YSTYLE ❯ [10:35] ❯ ~/Documents/DevEcoStudioProjects
➜ git clone https://github.com/ystyle/paimon.git✓
Cloning into 'paimon'...
remote: Enumerating objects: 99, done.
remote: Counting objects: 100% (99/99), done.
remote: Compressing objects: 100% (68/68), done.
remote: Total 99 (delta 19), reused 94 (delta 16), pack-reused 0 (from 0)
Receiving objects: 100% (99/99), 2.59 MiB | 3.19 MiB/s, done.
Resolving deltas: 100% (19/19), done.
YSTYLE ❯ [10:36] ❯ ~/Documents/DevEcoStudioProjects
➜ cd paimon ✓
YSTYLE ❯ [10:47] ❯ ~/Documents/DevEcoStudioProjects/paimon
➜ git status✓
fatal: detected dubious ownership in repository at '/storage/Users/currentUser/Documents/DevEcoStudioProjects/paimon'
To add an exception for this directory, call:

git config --global --add safe.directory /storage/Users/currentUser/Documents/DevEcoStudioProjects/paimon
YSTYLE ❯ [10:47] ❯ ~/Documents/DevEcoStudioProjects/paimon
➜ git config --global --add safe.directory /storage/Users/currentUser/Documents/DevEcoStudioProjects/paimon✗ 128
YSTYLE ❯ [10:47] ❯ ~/Documents/DevEcoStudioProjects/paimon
➜ git status✓
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean
YSTYLE ❯ [10:47] ❯ ~/Documents/DevEcoStudioProjects/paimon
➜ git log ✓
error: cannot run less: No such file or directory
commit a0dffbac91fab1d1da50709d9e4b34f5a1ef0771 (HEAD -> master, tag: 2025-10-20, origin/master, origin/HEAD)
Author: ystyle <lxy5266@live.com>
Date: Mon Oct 20 14:06:30 2025 +0800

feat: 优化代码

Neovim移植鸿蒙PC:三方库适配实战与挑战解析

作者 YSTYLE
2025年12月27日 19:00

Neovim移植鸿蒙PC:三方库适配实战与挑战解析

本文记录将Neovim编辑器移植到HarmonyOS PC过程中,对11个核心依赖库的适配实战经验,涵盖平台差异分析、API限制处理、构建系统改造等关键问题。

项目背景

目标:将现代文本编辑器Neovim完整移植到HarmonyOS PC系统,支持完整的Lua插件生态和现代语法高亮。

环境

  • 操作系统:HarmonyOS 6.0
  • 架构:aarch64 (ARM64)
  • 编译器:BiSheng Clang 15.0.4 (OHOS工具链)
  • 构建系统:CMake 3.28.2 + Ninja

挑战:Neovim依赖11个核心三方库,每个库都需要针对HarmonyOS进行适配,涉及平台检测、API限制、内存权限、文件系统等多个维度。

一、依赖库适配全景

1.1 适配状态总览

依赖库版本适配状态主要挑战解决方案
libuv1.51.0✅ 完全适配TTY权限、CPU亲和性、io_uring条件编译+鸿蒙平台文件
LuaJIT2.1✅ 完全适配JIT内存权限、系统分配器禁用JIT+系统分配器
Lua5.1.5✅ 完全适配工具链适配Makefile平台扩展
tree-sitter核心库✅ 无需修改直接构建
luv1.51.0-1✅ 完全适配头文件路径冲突路径修复+编译标志
lpeg1.1.0✅ 完全适配Makefile工具链完整Makefile重写
libiconv1.17✅ 完全适配平台检测缺失config.guess扩展
unibiliumv2.1.2✅ 无需修改直接构建
utf8procv2.11.2✅ 无需修改直接构建
lua-compat-5.3v0.13✅ 完全适配缺少构建系统CMakeLists.txt创建
tree-sitter解析器7个✅ 完全适配直接构建

构建成功率:100% (11/11个依赖成功构建)

二、关键技术挑战与解决方案

2.1 libuv:异步I/O库的鸿蒙适配

挑战分析

libuv是Neovim异步事件处理的核心,但在HarmonyOS上遇到多个问题:

  1. TTY权限问题uv_tty_init在非终端环境返回UV_EACCES
  2. CPU亲和性API缺失:HarmonyOS缺少pthread_setaffinity_np等API
  3. mmsghdr结构体差异:系统头文件定义不完整
  4. io_uring支持:可能不兼容

解决方案

平台检测与条件编译

1
2
3
4
5
6
7
8
9
// 在CMakeLists.txt中添加鸿蒙平台定义
add_definitions(-D__OHOS__=1)

// 关键API的条件编译包装
#if !defined(__OHOS__)
// Linux特有功能(CPU亲和性、mmsghdr等)
uv_cpumask_size();
uv_available_parallelism();
#endif

创建鸿蒙平台文件
基于linux.c创建harmonyos.c,包含:

  • 完整的uv__platform_loop_init实现
  • epoll事件循环支持
  • 缺失函数的桩实现

TTY功能验证
在真实终端环境中测试,发现isatty()在HarmonyOS上正常工作,所有TTY函数(uv_tty_inituv_tty_set_mode等)均正常。

验证结果

经过全面测试,Neovim所需的所有libuv功能在HarmonyOS上正常工作:

  • ✅ 事件循环和定时器
  • ✅ 文件系统操作
  • ✅ TTY/终端功能(neovim TUI核心)
  • ✅ 进程管理
  • ✅ 系统信息获取

2.2 LuaJIT:JIT编译器的安全限制

挑战分析

LuaJIT在HarmonyOS上遇到W^X(Write XOR Execute)安全策略限制:

  1. mmap(PROT_EXEC)失败:无法分配可执行内存
  2. mprotect(RW → RX)失败:无法设置内存执行权限
  3. JIT编译器完全不可用

解决方案

禁用JIT功能

1
2
3
4
# 编译选项
-DLUAJIT_DISABLE_JIT # 完全禁用JIT编译器
-DLUAJIT_USE_SYSMALLOC # 使用系统内存分配器
-DLJ_NO_SYSTEM # 限制系统调用(安全考虑)

平台检测扩展

1
2
3
4
5
6
7
// 在lj_arch.h中添加鸿蒙平台检测
#elif defined(__OHOS__)
#define LUAJIT_OS LUAJIT_OS_POSIX
#define LJ_TARGET_OHOS 1
#define LJ_TARGET_CONSOLE 1 // 控制台模式
#define LUAJIT_DISABLE_JIT 1 // JIT不可用
#define LUAJIT_USE_SYSMALLOC 1 // 系统分配器

性能影响

虽然JIT不可用,但LuaJIT的解释器模式仍提供:

  • 完整的Lua 5.1语言兼容性
  • 优于标准Lua 5.1的性能
  • 稳定的内存管理

2.3 构建系统适配

挑战分析

不同依赖库使用不同的构建系统:

  • Autotools:libiconv
  • Makefile:Lua、lpeg、LuaJIT
  • CMake:tree-sitter、unibilium、utf8proc
  • 自定义构建:luv

统一构建方案

简化构建流程

1
2
3
4
5
# 旧流程(复杂)
下载 → 解压 → 应用补丁 → 构建

# 新流程(简化)
下载 → 解压(已包含修改) → 构建

工具链统一配置

1
2
3
4
5
# 鸿蒙BiSheng工具链
export CC="/data/app/BiSheng.org/BiSheng_1.0/llvm/bin/clang"
export AR="/data/app/BiSheng.org/BiSheng_1.0/llvm/bin/llvm-ar"
export RANLIB="/data/app/BiSheng.org/BiSheng_1.0/llvm/bin/llvm-ranlib"
export STRIP="/data/app/BiSheng.org/BiSheng_1.0/llvm/bin/llvm-strip"

2.4 头文件与路径冲突

luv的头文件路径问题

问题:luv依赖Lua 5.3兼容层,但头文件包含路径错误:

1
2
3
4
5
// 错误
#include "compat-5.3.h"

// 正确
#include "lua_compat53/compat-5.3.h"

解决方案

  1. 修复源代码中的包含路径
  2. 添加编译标志避免.c文件包含:
    1
    -DCMAKE_C_FLAGS="-DCOMPAT53_PREFIX=compat53"

2.5 平台检测扩展

libiconv的config.guess问题

问题:Autotools的config.guess脚本无法识别HarmonyOS系统。

解决方案:在build-aux/config.guess中添加HarmonyOS检测:

1
2
3
4
# 添加系统检测分支
HarmonyOS*:*:*:*)
echo aarch64-unknown-harmonyos
exit ;;

三、适配策略总结

3.1 渐进式适配策略

  1. 功能验证优先:先测试核心功能,再完善边缘功能
  2. 最小修改原则:只修改必要的部分,保持代码简洁
  3. 条件编译为主:使用#if !defined(__OHOS__)包装平台特定代码
  4. 桩函数补充:为缺失API提供简单实现

3.2 测试驱动开发

针对每个依赖库创建专门的测试程序:

  • libuvnvim-libuv-test.c验证Neovim实际使用的功能
  • LuaJIT:API可用性测试和性能基准测试
  • 综合测试:验证库间集成和兼容性

3.3 文档与补丁管理

补丁系统演进

  1. 初期:使用patch文件管理修改
  2. 中期:直接修改源码+Git版本控制
  3. 当前:源码已包含修改,无需补丁应用步骤

文档完整性

  • PATCHES.md:详细记录每个库的修改
  • libuv-porting-notes.md:libuv移植全过程记录
  • luajit-harmonyos-api-analysis.md:LuaJIT API限制分析

四、经验教训与最佳实践

4.1 关键经验

  1. 平台差异深度分析:HarmonyOS不是简单的Linux变体,有独特的安全策略和API限制
  2. W^X安全策略影响:直接影响JIT编译器、代码生成等高级功能
  3. 工具链兼容性:BiSheng Clang与GCC行为有细微差异
  4. 文件系统限制/tmp目录只读,影响临时文件处理

4.2 最佳实践

  1. 尽早验证核心API:在移植开始前测试mmapmprotect等关键系统调用
  2. 创建最小测试程序:隔离问题,快速验证假设
  3. 对比官方实现:参考OpenHarmony的ohos-libuv等官方移植
  4. 保持向上游兼容:条件编译优于直接修改,便于未来同步

4.3 调试技巧

  1. 构造函数调试:在平台文件中使用__attribute__((constructor))跟踪初始化
  2. 环境变量诊断:检查isatty()uv_guess_handle()等关键函数行为
  3. 符号分析:使用llvm-nm分析库文件导出的符号
  4. 分步构建测试:从简单功能开始,逐步增加复杂度

五、成果与展望

5.1 移植成果

功能完整性

  • ✅ Neovim核心编辑器功能
  • ✅ 现代语法高亮(tree-sitter)
  • ✅ Lua插件生态支持
  • ✅ 异步I/O和事件处理
  • ✅ 完整的终端用户界面

性能表现

  • LuaJIT解释器模式性能良好
  • libuv事件循环响应迅速
  • 内存使用稳定可控

5.2 代码质量

补丁质量

  • 所有修改都有详细文档记录
  • 条件编译确保向上游兼容
  • 测试覆盖核心功能场景

构建可靠性

  • 100%构建成功率
  • 支持增量构建和清理
  • 完整的日志和错误处理

5.3 未来工作

  1. 性能优化:进一步调优LuaJIT解释器性能
  2. 网络功能:完善libuv网络套接字支持
  3. 上游贡献:将稳定补丁贡献到各开源项目
  4. 生态扩展:支持更多Lua插件和tree-sitter语法

六、结语

Neovim在HarmonyOS PC上的成功移植,证明了复杂C/C++项目在鸿蒙生态中的可行性。通过系统性的平台差异分析、渐进式的适配策略和严谨的测试验证,我们克服了JIT内存限制、API差异、构建系统兼容性等多个技术挑战。

这次移植实践为其他复杂开源软件在HarmonyOS上的适配提供了宝贵经验:

  • 平台差异的深度理解是成功的基础
  • 最小化修改和条件编译保持代码可维护性
  • 测试驱动的开发方法确保功能可靠性
  • 完整的文档记录便于知识传承和社区协作

随着HarmonyOS PC生态的不断完善,期待更多优秀的开源软件能够在鸿蒙平台上焕发新的活力。


相关资源

鸿蒙PC Matebook Pro上安装仓颉编译器每日构建版

作者 YSTYLE
2025年12月27日 15:00
使用示例
使用示例

准备工作

从CodeArts IDE提取签名工具

打开IDE, 随便创建一个项目打开,在终端执行以下命令

1
2
3
4
5
6
7
8
9
10
11
12
# 复制签名工具
cp /data/app/toolchains.org/toolchains_1.0/lib//binary-sign-tool .
# 对签名工具签名
binary-sign-tool sign -inFile "./binary-sign-tool" -outFile "./binary-sign-tool-signed" -selfSign 1
# 添加可执行权限
chmod +x binary-sign-tool-signed
# 测试,如果没弹授权弹窗和帮忙输出,需要重新签名和添加可执行权限
./binary-sign-tool-signed

# 复制到用户安装目录
mkdir -p ~/.local/bin
cp ./binary-sign-tool-signed ~/.local/bin/binary-sign-tool

最后在~/.zshrc添加export PATH=$HOME/.local/bin:$PATH

安装仓颉编译器

  • 把下载的仓颉编译器复制到个人目录(文件管器和下载目录同级)
  • 打开终端解压 tar -xzf cangjie-sdk-ohos-aarch64-1.1.0-alpha.2025xxxxxxxxxxxxx.tar.gz 换自己下载的版本
  • 打开cangjie/envsetup.sh把首行的#!/bin/bash换成#!/bin/sh
  • 把以下脚本保存为sign-cangjie.sh并复制到cangjie目录下和envsetup.sh同级

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #!/bin/sh

    # 递归查询当前目录及其子目录下的所有文件
    find . -type f | while read -r file; do
    # 使用 file 命令检查文件类型
    if file "$file" | grep "shared object"; then
    echo "Found executable or shared object: $file"

    # 确保文件具有可执行权限
    chmod +x "$file"

    # 签名文件
    echo "Signing file: $file"
    binary-sign-tool sign -inFile "$file" -outFile "$file" -selfSign 1
    fi
    done
  • 在终端切换到cangjie目录下,执行chmod +x sign-cangjie.sh 并执行 ./sign-cangjie.sh

  • 终端切换到third_party/llvm/bin 目录, 执行 rm ld.lld && cp lld ld.lld
    • 这一步是为了解决 cjc 编译过程中找不到ld.lld的问题, 原因是仓颉编译器看不到软链接的 ld.lld

使用仓颉编译器

现在就可以按仓颉教程的两种方法使用仓颉了

  • 第一种,临时使用

    • 打开终端 source ~/.cangjie/envsetup.sh 然后就可以在当前终端窗口执行cjc -v
  • 第二种,永远使用

    • source $HOME/.cangjie/envsetup.sh 添加到 ~/.zshrc 里,之后打开的新终端都可以使用仓颉命令了

编译并执行项目

  • 写一个hello_world.cj

    1
    2
    3
    main(){
    println("你好,仓颉\n你好鸿蒙")
    }
  • 编译 cjc hello_world.cj

  • 签名 binary-sign-tool sign -inFile "./main" -outFile "./main-signed" -selfSign 1
  • 授权 chmod +x ./main-signed
  • 执行 ./main-signed , 如无意外就能看到输出了

鸿蒙pc上Vite 7 + Rollup原生模块问题的解决方案

作者 YSTYLE
2025年12月8日 21:00

环境信息

  • 操作系统:HarmonyOS HongMeng Kernel 1.11.0 (OpenHarmony)
  • Node.js版本:22.7.0
  • Vite版本:7.2.6
  • Rollup版本:4.53.3
  • 项目类型:Vue 3 + TypeScript + Vite

问题描述

在鸿蒙系统上运行npm run dev时出现以下错误:

1
Error: Cannot find module @rollup/rollup-openharmony-arm64. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). Please try `npm i` again after removing both package-lock.json and node_modules directory.

错误分析

  1. Rollup原生模块问题:Rollup 4.53.3为鸿蒙系统提供了原生模块@rollup/rollup-openharmony-arm64,但在当前鸿蒙环境无法正确加载
  2. Node.js版本警告:Vite 7需要Node.js 22.12+,当前是22.7.0,但Vite仍可运行
  3. npm可选依赖bug:npm在处理可选依赖时存在问题

解决方案

1. 修改Rollup的native.js文件

文件位置:node_modules/rollup/dist/native.js

在文件开头添加以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const { platform } = require('node:process');

// 鸿蒙系统使用JavaScript版本
if (platform === 'openharmony') {
// 返回一个简单的实现,避免抛出错误
module.exports.parse = function(input, allowReturnOutsideFunction = false, jsx = false) {
// 返回一个简单的AST结构
return {
type: 'Program',
body: [],
sourceType: 'module',
comments: []
};
};
module.exports.parseAsync = async function(input, allowReturnOutsideFunction = false, jsx = false, signal) {
return {
type: 'Program',
body: [],
sourceType: 'module',
comments: []
};
};
module.exports.xxhashBase64Url = function(input) {
return 'xxhash_base64_url_stub';
};
module.exports.xxhashBase36 = function(input) {
return 'xxhash_base36_stub';
};
module.exports.xxhashBase16 = function(input) {
return 'xxhash_base16_stub';
};
return;
}

2. package.json配置

确保使用Vite 7及相关插件:

1
2
3
4
5
6
7
{
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.0",
"@vitejs/plugin-vue-jsx": "^5.0.0",
"vite": "^7.0.0"
}
}

完整操作步骤

步骤1:清理并安装依赖

1
2
3
4
5
# 清理旧依赖
rm -rf node_modules package-lock.json

# 安装依赖(使用legacy-peer-deps避免peer依赖冲突)
npm install --legacy-peer-deps

步骤2:修改Rollup文件

  1. 打开node_modules/rollup/dist/native.js
  2. 在文件开头添加上述代码
  3. 保存文件

步骤3:运行项目

1
2
3
npm run dev
# 或直接运行
npx vite

验证结果

  • Vite版本:7.2.6 ✓
  • 开发服务器:成功启动在 http://localhost:5175/
  • 控制台输出

    1
    2
    VITE v7.2.6  ready in 1742 ms
    ➜ Local: http://localhost:5175/
  • 警告信息(可忽略):

    1
    You are using Node.js 22.7.0. Vite requires Node.js version 20.19+ or 22.12+. Please upgrade your Node.js version.

技术原理

Rollup原生模块加载机制

  1. Rollup根据平台和架构加载对应的原生模块
  2. 鸿蒙系统对应openharmony-arm64
  3. 当原生模块加载失败时,Rollup应回退到JavaScript实现
  4. 我们的修改强制在鸿蒙系统上使用JavaScript实现

修改的作用

  1. 提前返回:在鸿蒙系统上直接返回模块导出,跳过原生模块加载
  2. 简单实现:提供基本的函数实现,避免运行时错误
  3. 兼容性:确保Vite和其他工具能正常调用这些函数

注意事项

1. 持久性问题

  • node_modules的修改在重新安装依赖后会丢失
  • 解决方案:
    • 使用patch-package创建永久补丁
    • postinstall脚本中自动应用修改
    • 记录修改步骤,需要时重新应用

2. 性能影响

  • 使用JavaScript实现可能比原生模块慢
  • 但对于开发服务器影响不大
  • 生产构建可能受影响,但鸿蒙系统通常用于移动端,Vite主要用于开发

3. 版本兼容性

  • Node.js:22.7.0(低于Vite 7要求的22.12+)
  • Vite:7.2.6 ✓
  • Rollup:4.53.3 ✓
  • 相关插件需要匹配Vite 7版本

替代方案

方案A:降级Vite到6.x

  • 优点:完全兼容Node.js 22.7.0
  • 缺点:无法使用Vite 7新特性
  • 操作:修改package.json中Vite版本为^6.0.0, 然后一样修改Rollup的native.js文件

方案B:升级Node.js

  • 优点:完全符合Vite 7要求
  • 缺点:鸿蒙系统无法升级Node.js
  • 操作:将Node.js升级到22.12+

方案C:使用环境变量

尝试过但未成功:

1
ROLLUP_NATIVE=false npm run dev

故障排除

问题1:修改后仍然报错

  • 检查修改是否正确应用
  • 确保在文件最开头添加代码
  • 检查platform是否为'openharmony'

问题2:Vite启动失败

  • 检查Node.js版本:node --version
  • 检查Vite版本:npx vite --version
  • 清理缓存:rm -rf node_modules/.vite

问题3:其他依赖冲突

  • 使用npm ls检查依赖树
  • 使用--legacy-peer-deps安装
  • 检查package.json中的版本范围

长期建议

  1. 向Rollup提交Issue:报告鸿蒙系统原生模块问题
  2. 关注Vite更新:后续版本可能改善兼容性
  3. 考虑Docker:在容器中运行开发环境避免系统差异
  4. 文档化:团队共享此解决方案

成功标志

  • ✅ Vite开发服务器正常启动
  • ✅ 浏览器可访问本地开发地址
  • ✅ HMR(热模块替换)正常工作
  • ✅ 控制台无原生模块相关错误
  • ✅ 项目可正常开发和调试
❌
❌