JIEBA分词WASM版本编译全过程

摘要

本文介绍了在windows环境下编译jieba分词WASM版本的完整过程

前言

我开发了一个浏览器插件,其中使用了jieba分词的WASM版本。但是在上架火狐的时候碰到了点小麻烦,火狐浏览器对于要上架的扩展,必须提供所有代码(包括打包成了wasm的代码),并要提供打包编译了的代码的打包流程[1]。而我用了WASM方案的jieba分词的,所以,我便尝试本地从源码构建并打包一个Jieba Wasm,我将我编译打包的所有过程,以及踩的坑全部记录在这里,希望对大家有所帮助。

相关知识

在开始编译打包之前,我们最好了解一下相关的知识

什么是jieba分词

Jieba分词(项目地址:https://github.com/fxsjy/jieba)是一个开源的基于Python的分词程序,能将输入的句子进行切割,切割成一个一个词组。比如:

欢迎访问我的网站

切割为

欢迎 / 访问 / 我 / 的 / 网站

什么是WASM

WASM(相关文档https://webassembly.github.io/spec/),全称WebAssembly技术,是一个可以运行在浏览器中的二进制格式,相比起传统的在浏览器端执行的js,WASM执行速度更快,性能更好。我们可以将不同的语言的程序(比如说C、Rust、Go等)编译为WASM并且提供给浏览器,让这些代码能够在网页上进行高性能执行。

关于RUST

RUST是由Mozilla开发的 [2] ,一个专注于内存安全的语言(官网https://rust-lang.org/),致力于高性能与尽量保证内存安全。我列了一张表(表1)给大家对比它与传统语言的区别。

运行速度内存安全
C++/C速度快容易因为程序员的疏忽产生内存安全问题
Python速度偏慢有自动内存垃圾回收,内存安全问题出现较少
Rust速度快在编译时检查代码中的内存访问,尽量减少编译出来的程序出现内存安全问题
表1 三种编程语言之间部分特性的对比

编译实现思路

要将Jieba分词打包为适用于Web的Wasm版本,整体大概的流程是:先将Jieba转化为Rust语言版本(项目地址https://github.com/messense/jieba-rs),再将程序打包从Rust程序转化为通用的Wasm版本,最后转化为适配于Web平台的Wasm版本(项目地址https://github.com/fengkx/jieba-wasm)。

这里说一下我的猜测,为什么项目作者不用Python版本的原版Jieba分词直接打包成Wasm,而是使用Rust版本打包成Wasm。诚然,Python是可以打包成Wasm,但是受限于API限制,很多库是无法进行使用的,而且打包需要将整个解释器全部塞进去,包体积很大,且在浏览器端启动慢。而Rust打包成Wasm启动速度快,且体积小。所以项目作者使用Rust版本的Jieba分词。

基础环境准备

我们先要开始安装下基础的环境

rust环境

前往rust官网的下载地址,链接:https://rust-lang.org/tools/install/。官网下载界面如图1所示,其中红色方框圈起来的就是下载按钮。点击下载。

Rust官网的下载界面截图
图1 Rust官网的下载界面截图

下载完后,打开下载的安装程序,会显示如图2所示的黑色窗口。因为Rust需要链接器以及相关的API库,所以Rust安装程序会询问,是否安装Visual Studio Community。这里输入1后回车进行安装。

Rust安装器询问是否安装Visual Studio Community installer的截图
图2 Rust安装器询问是否安装Visual Studio Community installer的截图

输入1后稍等一会,会弹出Visual Studio Installer,如图3所示,点击继续即可。

弹出的 Visual Studio Installer安装器界面
图3 弹出的 Visual Studio Installer安装器界面

当Visual Studio安装完后,回到Rust安装程序的黑窗口对话框,如图4。这时候Rust安装器会问你是否是标准安装,还是自定义安装。为了避免后续编译出现一些莫名其妙的错误,我这里推荐标准安装(直接按回车就OK了)。

Rust安装程序询问安装模式截图
图4 Rust安装程序询问安装模式截图

安装过程,Rust会下载大量东西,等安装完成,Rust安装程序会显示“stable-x86_64-pc-windows-msvc installed”,如图5。

安装Rust成功后界面的截图
如图5 安装Rust成功后界面的截图

安装完成后,我们新开一个终端(请注意,不要使用安装之前的终端,因为安装的时候Rust安装程序修改了环境变量,安装之前打开的终端并没有刷新环境变量,会导致后续步骤出错),输入下面的命令,测试是否正确安装,输入后如果安装正确的话,应该会输出rustc以及cargo[3]的版本号,如图6所示

rustc --version
cargo --version
rustc与cargo正确安装时候,输入命令后系统的输出截图
图6 rustc与cargo正确安装时候,输入命令后系统的输出截图

NodeJs环境

编译过程还需要Node环境,首先前往NodeJs的官网的下载界面(https://nodejs.org/en/download),点击 Windows Installer,下载Msi包(下载按钮我在图7中用红方框圈出来了)

 NodeJs官网的下载界面截图
图7 NodeJs官网的下载界面截图

下载好后,是一个msi文件,双击打开,并开始安装。安装过程如图8、图9、图10。

NodeJs的安装程序的欢迎页面
图8 NodeJs的安装程序的欢迎页面

一路点击Next,直到如图9 有Install按钮,点击Install进行安装

NodeJs安装程序的准备安装界面
图9 NodeJs安装程序的准备安装界面

安装完成后,界面应如图10所示

NodeJs安装程序 安装完成的界面截图
图10 NodeJs安装程序 安装完成的界面截图

安装完成后,仍然是新开一个对话框(不要用安装之前打开的对话框,原因同上),输入node,看下终端是否有输出”Welcome to Node.js ….”,如图11所示。如果有,代表NodeJs成功安装。

NodeJs正确安装后测试输出的结果截图
图11 NodeJs正确安装后测试输出的结果截图

CLang 安装

虽然前面安装了Msvc,但是由于编译过程中某一个组件依赖了Clang,如果系统中没有Clang,后续编译会报错(这个报错我会在后面编译过程中提到)。所以我们还是需要安装下Clang。

Clang有多种方案安装,可以通过Visual Studio进行安装,也可以通过官网安装,还可以通过windows上面的包管理器进行安装。我这边使用的是官网安装的方式。

我们前往llvm的官网发布页面[4],官网下载页面的地址是:https://releases.llvm.org,在Download栏目中,选择一个较新的版本,点击download,如图12所示。

Clang官网的发布页面的Download栏目
图12 Clang官网的发布页面的Download栏目

点击Download后,会跳转到github的Release界面。找到windows installer 选项,点击这个选项对应的链接,下载安装器(installer下载按钮所处的地方我已经在图13中用红色方框框出来了)

LLVM在github上的release页面,红框圈出的是LLVM安装器的按钮
图13 LLVM在github上的release页面,红框圈出的是LLVM安装器的按钮

打开下载好的LLVM安装程序,安装程序启动如图14所示,一路点击下一步。

LLVM安装程序开始界面截图
图14 LLVM安装程序开始界面截图

当安装程序提示,是否将LLVM添加到环境变量(界面如图15所示),强烈建议你将LLVM添加到环境变量之中(如果你不想后面手动添加修改环境变量的话),选择”Add LLVM to the system Path for all users”(为所有用户添加LLVM环境变量)即可,选中后点击下一步。

LLVM安装程序 询问是否添加到环境变量的截图
图15 LLVM安装程序 询问是否添加到环境变量的截图

安装完后,仍然是新开一个窗口,验证下Clang是否正确安装,验证命令在下方给出了。输入后若安装正确,屏幕应该输出“clang version….”等输出,如图16所示。

clang --version
 clang正确安装后,测试的输出结果截图
图16 clang正确安装后,测试的输出结果截图

编译过程

下载编译所需要的代码

前往github,将下面两个项目代码全部下载下来

jieba-rs:https://github.com/messense/jieba-rs (该项目将jieba改造成rust版本)

jieba-wasm:https://github.com/fengkx/jieba-wasm(该项目将jieba打包成wasm)

安装编译需要的组件 wasm-bindgen-cli

wasm-bindgen-cli是用于对编译出来的wasm进行处理的一个工具,生成方便js与ts调用的相关代码。下载命令如下。

cargo install wasm-bindgen-cli --locked

如果运行过程报link.exe not found,详细报错输出跟下面类似的话,这是没有使用develper powershell导致的。

error: linker `link.exe` not found
  |
  = note: program not found

note: the msvc targets depend on the msvc linker but `link.exe` was not found

note: please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option.

note: VS Code is a different product, and is not sufficient.

安装Visual Studio的时候,默认不会将link.exe等程序写入到系统的环境变量中,但是Visual Studio安装后,会附带有一个叫做developer powershell的工具,这个工具将会在启动的时候自动配置好msvc相关编译器与相关依赖组件的环境变量。[5]。我们在开始菜单中 搜索Developer Powershell (其实使用Developer Command Prompte命令提示符也是可以的,但下文中都是以Powershell为例子),打开即可,在菜单中搜索developer Powershell的结果如图17所示。下文中的操作,请都在developer powershell中执行。

在开始菜单搜索Developer Powershell的结果的截图
图17 在开始菜单搜索Developer Powershell的结果的截图

打开Developer Powershell后,应打开了一个跟图18类似的powershell窗口(终端开头应会输出”Developer PowerShell的字样)。此时我们输入一下link.exe ,看当前环境中link.exe是否配置正确,如正确配置,输出应与图18中截图的输出类似。

Developer Powershell正确调用link.exe的截图
图18 Developer Powershell正确调用link.exe的截图

此时重新运行cargo install wasm-bindgen-cli --locked

如果出现报错cannot open input file 'kernel32.lib(详细报错信息如下),这是因为Rust安装Visual Studio的时候默认没有安装windows 11/10的SDK,所以无法正常工作。

error: could not compile `serde` (build script) due to 1 previous error
error: could not compile `zerocopy` (build script) due to 1 previous error
error: linking with `link.exe` failed: exit code: 1181
  |
  = note: "link.exe" "/NOLOGO" "C:\\Users\\q2019715\\AppData\\Local\\Temp\\rustc7VqXvn\\symbols.o" "<2 object files omitted>" "<sysroot>\\lib\\rustlib\\x86_64-pc-windows-msvc\\lib/{libstd-*,libpanic_unwind-*,libcfg_if-*,libwindows_targets-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libunwind-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "kernel32.lib" "kernel32.lib" "kernel32.lib" "ntdll.lib" "userenv.lib" "ws2_32.lib" "dbghelp.lib" "/defaultlib:msvcrt" "/NXCOMPAT" "/OUT:C:\\Users\\q2019715\\AppData\\Local\\Temp\\cargo-installN6DhWV\\release\\build\\icu_normalizer_data-0e4ce4f2ac102e3c\\build_script_build-0e4ce4f2ac102e3c.exe" "/OPT:REF,NOICF" "/DEBUG" "/PDBALTPATH:%_PDB%" "/NATVIS:<sysroot>\\lib\\rustlib\\etc\\intrinsic.natvis" "/NATVIS:<sysroot>\\lib\\rustlib\\etc\\liballoc.natvis" "/NATVIS:<sysroot>\\lib\\rustlib\\etc\\libcore.natvis" "/NATVIS:<sysroot>\\lib\\rustlib\\etc\\libstd.natvis"
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: LINK : fatal error LNK1181: cannot open input file 'kernel32.lib'␍

解决方案也比较简单,在开始菜单中找到Visual Studio Installer这个软件(这个软件在刚刚安装Visual Studio的时候会自动安装),如图19,点击修改按钮。

Visaul Studio installer软件截图
图19 Visual Studio installer软件截图

然后在单个组件选项卡中找到Windows 11 SDK(如果是Windows10或者其他版本的Windows的话,勾选上对应系统版本的SDK),如图20所示。

Visual Studio在单个组件中勾选 Windows 11 SDK的截图
图20 Visual Studio在单个组件中勾选 Windows 11 SDK的截图

点击修改,稍等片刻完成安装后,重新执行失败的安装命令cargo install wasm-bindgen-cli --locked。这次应该能够成功安装完这个组件,安装成功的截图如图21所示。

wasm-bindgen-cli安装成功的截图
图21 wasm-bindgen-cli安装成功的截图

我们也测试下是否正确安装,测试命令如下,输入之后,应该有如图22一样的输出。

wasm-bindgen --version
wasm-bindgen --version 成功运行截图
图22 wasm-bindgen –version 成功运行截图

安装编译所需要的组件 wasm-opt

wasm-opt是一个用于优化生成的wasm文件的程序,这也是我们编译jieba wasm版本中需要安装的组件。

正常情况下安装命令如下。(github项目 jieba-wasm的readme中也是说这样安装的)

cargo install wasm-opt --locked

但是如果我们执行这个安装命令的话,会出现如图23的报错

执行cargo install wasm-opt的报错截图
图23 执行cargo install wasm-opt的报错截图

详细的报错内容如下:

  error occurred: Command "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.44.35207\\bin\\HostX64\\x64\\cl.exe" "-nologo" "-MD" "-O2" "-Brepro" "-I" "C:\\Users\\q2019715\\AppData\\Local\\Temp\\cargo-installfcDy0e\\release\\build\\wasm-opt-sys-7ebe7db34fa31d38\\out\\cxxbridge\\include" "-I" "C:\\Users\\q2019715\\AppData\\Local\\Temp\\cargo-installfcDy0e\\release\\build\\wasm-opt-sys-7ebe7db34fa31d38\\out\\cxxbridge\\crate" "-I" "C:\\Users\\q2019715\\.cargo\\registry\\src\\index.crates.io-1949cf8c6b5b557f\\wasm-opt-sys-0.116.0\\binaryen\\src" "-I" "C:\\Users\\q2019715\\.cargo\\registry\\src\\index.crates.io-1949cf8c6b5b557f\\wasm-opt-sys-0.116.0\\binaryen\\src\\tools" "-I" "C:\\Users\\q2019715\\AppData\\Local\\Temp\\cargo-installfcDy0e\\release\\build\\wasm-opt-sys-7ebe7db34fa31d38\\out" "-I" "C:\\Users\\q2019715\\.cargo\\registry\\src\\index.crates.io-1949cf8c6b5b557f\\wasm-opt-sys-0.116.0\\binaryen\\third_party/llvm-project\\include" "-W4" "/std:c++17" "/w" "/DTHROW_ON_FATAL" "/DBUILD_LLVM_DWARF" "/DNDEBUG" "-FoC:\\Users\\q2019715\\AppData\\Local\\Temp\\cargo-installfcDy0e\\release\\build\\wasm-opt-sys-7ebe7db34fa31d38\\out\\4e4d2e940d1d64d9-fuzzing.o" "-c" "C:\\Users\\q2019715\\.cargo\\registry\\src\\index.crates.io-1949cf8c6b5b557f\\wasm-opt-sys-0.116.0\\binaryen\\src\\tools/fuzzing\\fuzzing.cpp" with args "cl.exe" did not execute successfully (status code exit code: 2).


error: failed to compile `wasm-opt v0.116.1`, intermediate artifacts can be found at `C:\Users\q2019715\AppData\Local\Temp\cargo-installfcDy0e`.
To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path.
cargo install wasm-opt --locked -vv 2>&1 | Tee-Object .\wasm-opt-build.log

然后我找了半天的编译日志,发现真正导致编译报错的原因在此(编译过程中有大量下面的报错):

2019:[wasm-opt-sys 0.116.0] C:\Users\q2019715\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\wasm-opt-sys-0.116.0\binaryen\src\literal.h(739): error C2039: 'isBasic': is not a member of 'wasm::Type'
12022:[wasm-opt-sys 0.116.0] C:\Users\q2019715\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\wasm-opt-sys-0.116.0\binaryen\src\literal.h(739): error C2039: 'isBasic': is not a member of 'wasm::Type'

这个是当前这个版本在windows上打包有点问题,要解决这个问题最快的解决方案是直接找一个打包好了的wasm-opt配置到电脑上面。(当然,硬要从源码上解决这个问题也不是不行,但是会非常非常麻烦,此方案本文不做过多介绍)

前往wasm-opt项目的发布地址(https://github.com/WebAssembly/binaryen/releases),找到一个带x86_64-windows版本的(注意,有的版本不带x86_64 windows,请不要下错了,尤其是不要下错成Windows Arm版本-除非你是Arm的windows),如图24所示, 红色框框圈出的是就是x86_64 windows版本。下载下来。

 wasm-opt在github上的发布页面
图24 wasm-opt在github上的发布页面

下载下来后,在一个适当的地方解压,解压出来的文件如图25所示。其中有一个文件夹名称叫做bin

wasm-opt包下载后解压出来的文件夹
图25 wasm-opt包下载后解压出来的文件夹

然后前往计算机的环境变量设置,在用户变量处选择Path,选中后点击点击编辑,在弹出的 编辑环境变量窗口中,点击新建按钮,新建一行,新建内容的值是我们刚刚解压的wasm-opt文件夹下面的bin目录的地址,如图26所示。

将wasm-opt放进用户环境变量流程截图
图26 将wasm-opt放进用户环境变量流程截图

然后添加完后,请一定要记得点击确定按钮。保存完后,在新的Developer Powershell中输入下面命令检查wasm-opt是否正确安装,如若正确安装,输出应与图27一致。

wasm-opt --version
验证wasm-opt是否正确安装时,安装正确的输出结果
图27 验证wasm-opt是否正确安装时,安装正确的输出结果

配置npm依赖

在下载的jieba-wasm的项目路径,执行下方的命令,安装npm依赖

npm install

如果不执行这个命令的话,开始编译会报“'wireit' 不是内部或外部命令,也不是可运行的程序
或批处理文件。”
等错误。

安装时候如若输出下面类型的提示,不必担心。其实依赖是已经安装成功了,这个是npm提示依赖链条有的依赖可能有安全问题,需要注意,但是我们这次是编译wasm的,可以暂且忽略这个问题。


2 vulnerabilities (1 moderate, 1 high)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

开始编译-生成wasm本体

现在这一步就是生成wasm本体,也就是通用的wasm模块。执行下述命令开始编译

npm run build:cargo

如果编译时候报下面的错误

PS F:\jieba-wasm-master> npm run build:cargo

> jieba-wasm@2.4.0 build:cargo
> wireit

    Updating crates.io index
error: failed to get `jieba-rs` as a dependency of package `jieba-rs-wasm v1.0.0 (F:\jieba-wasm-master)`

Caused by:
  failed to load source for dependency `jieba-rs`

Caused by:
  Unable to update F:\jieba-wasm-master\jieba-rs

Caused by:
  failed to read `F:\jieba-wasm-master\jieba-rs\Cargo.toml`

Caused by:
  系统找不到指定的文件。 (os error 2)

❌ [build:cargo] exited with exit code 101.
❌ 1 script failed.

这个是因为 下载jieba-wasm项目文件的时候没有下载jieba-rs文件夹下的东西。即rust版本的jieba并没有放到jieba-wasm的编译路径中。我们前往https://github.com/messense/jieba-rs 下载相关文件后,将文件放到jieba-wasm项目中的jieba-rs这个目录中,如图28所示。

jieba-wasm项目目录截图,图中选中的是jieba-rs文件夹
图28 jieba-wasm项目目录截图,图中选中的是jieba-rs文件夹

备注:如果是使用git进行拉取的话,git clone --recurse-submodules https://github.com/fengkx/jieba-wasm 也可以达到同样的效果。

再次进行编译,如果报下面的错误

   Compiling foldhash v0.2.0
   Compiling zmij v1.0.19
   Compiling equivalent v1.0.2
error[E0463]: can't find crate for `core`
  |
  = note: the `wasm32-unknown-unknown` target may not be installed
  = help: consider downloading the target with `rustup target add wasm32-unknown-unknown`

For more information about this error, try `rustc --explain E0463`.
error: could not compile `memchr` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...
error: could not compile `cfg-if` (lib) due to 1 previous error

❌ [build:cargo] exited with exit code 101.
❌ 1 script failed.

这个是因为现在rust要编译wasm32-unknown-unknown这个目标的结果,但是当前本地计算机没有安装这个目标对应的标准库与依赖,所以报错了。解决方案很简单,输入下面的命令,加载相关的库与依赖。指令执行成功后应该有如图29所示的结果。

rustup target add wasm32-unknown-unknown
wasm32-unknown-unknown相关依赖加载成功截图
图29 wasm32-unknown-unknown相关依赖加载成功截图

这个执行完后,再次执行npm run build:cargo ,如果报了下面的错误,那就是你Clang没有安装(前文已经提到了需要安装了Clang,请依照前文的需求,再次安装)。jieba-wasm中的项目依赖中有部分强行依赖了clang生态

  cargo:rerun-if-env-changed=CFLAGS_wasm32-unknown-unknown
  CFLAGS_wasm32-unknown-unknown = None

  --- stderr


  error occurred in cc-rs: failed to find tool "clang": program not found (see https://docs.rs/cc/latest/cc/#compile-time-requirements for help)


warning: build failed, waiting for other jobs to finish...

❌ [build:cargo] exited with exit code 101.
❌ 1 script failed.

安装完Clang后,输入下方指令清空下编译缓存,避免出现缓存污染的情况。

cargo clean

然后再次执行npm run build:cargo,这次应该能编译成功了,成功编译的截图如图30所示。

npm run build:cargo编译成功截图
图30 npm run build:cargo编译成功截图

编译第二步-生成各平台绑定代码

生成了wasm本体之后,编译是还没有完成。先前编译的wasm只是一个基础的内核,还不能直接给不同的js环境使用(当然,如果你乐意,可以自己手搓一个兼容层来进行使用),还需要进行各个平台的绑定,否则无法正确的运行。[6]

执行下述命令进行平台绑定,执行完成后截图如图31所示

npm run build
成功执行平台绑定的截图
图31 成功执行平台绑定的截图

执行成功后,可以在jieba-wasm的项目目录的\pkg\web文件夹 找到适配于web的jieba_rs_wasm_bg.wasm。\pkg\nodejs下面能找到适配nodejs的,还有各种其他环境的,如图32所示。这些编译成果文件我们就可以拿去调用了。至此,编译完成。

 编译结果文件截图
图32 编译结果文件截图

简易使用测试

编译完成后,我们可以测试下编译成果是否能够正常使用,执行下面的命令启动一个简单的测试环境

cd demo/web
npm install
npm run dev

运行完成后,程序会监听本地的3000端口,可以前往http://localhost:3000/ 进行访问测试,如图33所示。我们可以输入一段中文句子进行分词,点击分词按钮,网页会输出结果。输出结果,即代表编译成功。

测试编译好的jieba-wasm的网页截图
图33 测试编译好的jieba-wasm的网页截图

相关环境的版本

系统:Windows11家庭中文版 64位

Rust版本: 1.93.0 x86_64-pc-windows-msvc

Node版本:v24.13.0

LLVM版本:21.1.0

Visual Studio版本:2022

wasm-opt版本:124

wasm-bindgen-cli:0.2.108

使用的jieba-rs github提交Hash:7f3b2737

使用的jieba-Wasm github提交 Hash:56ddcb89

参考

  1. ^火狐关于这个要求相关的文档的链接在此:https://extensionworkshop.com/documentation/publish/source-code-submission/
  2. ^严格来说,Rust是早年前Graydon Hoare 发起的,然后由Mozilla 赞助孵化,现在由Rust Foundation 和社区主导发展的
  3. ^cargo是Rust的包管理器与构建工具,他对我们后面的编译过程至关重要
  4. ^LLVM是一套编译器与工具链的集合,一开始由伊利诺伊大学研发,Clang一般打包在LLVM安装包中,所以我们直接安装LLVM,Clang就一块安装好了
  5. ^如果想了解更多关于Developer powershell相关的信息,可以查看 Visual Studio的相关文档:https://learn.microsoft.com/zh-cn/visualstudio/ide/reference/command-prompt-powershell?view=vs-2022
  6. ^如果想要了解更多关于wasm平台绑定相关的内容,可以参阅https://wasm-bindgen.github.io/wasm-bindgen/reference/deployment.html这篇文章

--------------

本文标题为:

JIEBA分词WASM版本编译全过程

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,记得载明出处。
内容有问题?想与我交流下?点此哦,欢迎前来交流~
上一篇