第 22.12 节 安装 code-server 和 clangd

警告

本教程目前在 13.2-RELEASE 和 14.0-RELEASE 上测试正常,其他版本请慎重参考。

常见问题

为什么要有这篇教程?

  • 有了 code-server 就意味着不用安装桌面环境即可获得运行在原生 FreeBSD 环境上的集成开发环境

  • 利用熟悉的 vscode 界面和强大的 clangd 支援 FreeBSD 内核的开发

  • 这样可以大大降低投入 FreeBSD 代码贡献以及二次开发所需的学习成本

为什么要用 Arch Linux 兼容层?

  • 因为现在 FreeBSD 版的 code-server 被上游搞坏了几乎没法用

  • 纵观历史版本也只有一个 revision 是好的

  • 所以用 Linux 兼容层来运行是目前最节省时间节省生命的方案

  • 并且 code-server 自从基于 node18 以后就对 glibc 的最低版本做出了更高的要求

  • 源里那个 CentOS 运行时就运行不起它来了

兼容层?那还能用来搞 FreeBSD 的开发吗?

  • 当然可以

  • 虽然为了运行 code-server 我们用到了 Linux 兼容层

  • 但是 clangd 还有任何其他开发工具将全部由 FreeBSD 提供

为什么 clangd 还有任何其他开发工具将全部由 FreeBSD 提供?

  • 如你所见,出于综合因素考量现在 code-server 运行在 FreeBSD 的 Linux 二进制兼容模式下

  • 但首先我们清楚,这是 Linux 二进制兼容模式,不是 Linux 模拟器模式,更不是 Linux 虚拟机模式

  • 既然是 Linux 二进制兼容,那么尝试去运行 Linux 程序的主体一定还是 FreeBSD 内核本身,并没有额外多出来一个 Linux 内核

  • 既然主体还是 FreeBSD 内核,那么一定会涉及到 Linux 程序和 FreeBSD 程序混合运行的问题

  • 既然涉及到 Linux 程序和 FreeBSD 程序混合运行的问题,那么一定也会涉及调用 Linux 还是 FreeBSD 的动态链接库的问题

  • 对于一个已经编译好的二进制程序来说,它要用到什么动态链接库是写死了的

  • 假设一个 Linux 二进制程序依赖 /lib/glibc.so ,那么它一定会到 /lib/glibc.so 去寻找这个文件,绝对不会看向其他地方

  • 但是我们都知道在 FreeBSD 上 Linux 的运行时环境在 /compat/linux 下面

  • 这时候要么给 Linux 二进制程序打补丁,把依赖 /lib/glibc.so 改成依赖 /compat/linux/lib/glibc.so

  • 想想也都知道,要给每个 Linux 二进制程序打补丁,这样做难度有多大,而且总归会有漏网之鱼吧?

  • 那就试试第二个方法:在 FreeBSD 内核层面实现对路径的劫持

  • 当一个 Linux 二进制程序尝试去 open 某个文件,假使说就是这个程序尝试去调用 /lib/glibc.so 这个动态链接库的时候

  • FreeBSD 内核首先会给 /lib/glibc.so 这个路径前面加上 /compat/linux 变成 /compat/linux/lib/glibc.so

  • 这个过程对应用程序而言是透明的,即应用程序自己也不知道它拿到的到底是 /compat/linux/lib/glibc.so 还是 /lib/glibc.so

  • 用户还有 FreeBSD 内核站在上帝视角,可以知道它实际上拿到的是 /compat/linux/lib/glibc.so

  • 但如果尝试去 open 的不是 /compat/linux 下面有的文件,而是 /compat/linux 外面的某个文件呢?

  • 这个时候内核会自动 fallback 到程序传入的原始路径,尝试再 open 一次,并祈祷这次能 open 成功

  • 如果这次还是失败,那就真的没有这个文件, open 系统调用就失败了

  • 我们回到 code-server 这里

  • 已知 code-server 是一个 Linux 程序,当我们尝试打开一个文件或者目录,它一定是先去 /compat/linux 下面去找

  • 假设你要打开 /usr/src 这个目录,一般来说你是想要看 FreeBSD 的源码树,对吧?

  • 可如果 /compat/linux/usr/src 这个目录存在,那么被打开的实际上就会是 /compat/linux/usr/src 而不是你想要的 /usr/src

  • 这时候我们就需要把 /compat/linux/usr/src 这个目录删除掉,确保内核帮你 fallback 到真正的 /usr/src 上面去

  • 我们回到 clangd 这里

  • 已知 /compat/linux/bin/clangd 是不存在的

  • 那么当 code-server 尝试启动 clangd 的时候,一定会默认启动 /usr/local/bin/clangd 这个 clangd

  • 其他开发工具同理

  • 这就是为什么 clangd 还有任何其他开发工具将全部由 FreeBSD 提供

还有什么需要补充的内容?

  • 如何在服务器上通过 HTTPS 来提供 code-server 服务

  • 探讨 Linux 兼容层与 Linux Jail 究竟有何区别

有什么需要注意的?

  • 所有操作都使用 root 用户进行

  • 请勿跳步

你这是在鼓励用户当 root 敢死队?

  • 。。。

服务器启用 Linux 二进制兼容,并部署 archlinux-bootstrap 镜像

service linux enable
service linux start
fetch -o /tmp https://mirrors.cernet.edu.cn/archlinux/iso/latest/archlinux-bootstrap-x86_64.tar.gz
tar -C /tmp -xpf /tmp/archlinux-bootstrap-x86_64.tar.gz || true
cp -Rf /tmp/root.x86_64/* /compat/linux

服务器配置 pacman 源,并添加 archlinuxcn 仓库

cat >/compat/linux/etc/pacman.conf <<EOF
[options]
Architecture = auto
ParallelDownloads = 5

[core]
Server = https://mirrors.cernet.edu.cn/archlinux/\$repo/os/\$arch
SigLevel = Required DatabaseOptional

[extra]
Server = https://mirrors.cernet.edu.cn/archlinux/\$repo/os/\$arch
SigLevel = Required DatabaseOptional

[archlinuxcn]
Server = https://mirrors.cernet.edu.cn/archlinuxcn/\$arch
SigLevel = Required DatabaseOptional
EOF

服务器初始化 Arch Linux 运行时环境

chroot /compat/linux pacman-key --init
chroot /compat/linux pacman-key --populate

服务器更新 Arch Linux 运行时环境,并安装 code-server

cp /etc/resolv.conf /compat/linux/etc
chroot /compat/linux pacman -Syu --noconfirm
chroot /compat/linux pacman -S --noconfirm archlinuxcn-keyring
chroot /compat/linux pacman -S --noconfirm code-server

服务器删除 Arch Linux 运行时环境中的无用目录

rm -Rf /compat/linux/home
rm -Rf /compat/linux/root
rm -Rf /compat/linux/usr/local
rm -Rf /compat/linux/usr/src

服务器安装 llvm 与 clangd 插件

pkg install -y llvm
ln -sf /compat/linux/lib/code-server/bin/code-server /usr/local/bin
code-server --install-extension llvm-vs-code-extensions.vscode-clangd

服务器通过 daemon 命令启动 code-server

daemon -p /root/.code-server.pid -f code-server --auth=none

客户端通过 SSH 建立隧道并通过浏览器连接到 code-server 服务器

ssh -L 8080:127.0.0.1:8080 -N root@server

在浏览器中访问 http://127.0.0.1:8080

(示例)浏览器中用 code-server 打开 FreeBSD 的源码树

code-server /usr/src

(示例)浏览器中编译最小化内核并生成 compile_commands.json 文件

pkg install  bear
bear --append -- make KERNCONF=MINIMAL buildkernel

等待编译完成并生成 compile_commands.json 文件,然后你就可以开始阅读内核关键部分的源码了。

自动化安装脚本

为了便于读者快速获得开发环境,我们将安装 code-server 的步骤整理成一个脚本:

#!/bin/sh

set -e

ARCHLINUX_MIRROR="https://mirrors.cernet.edu.cn/archlinux"
ARCHLINUXCN_MIRROR="https://mirrors.cernet.edu.cn/archlinuxcn"
FREEBSD_PKG_MIRROR="https://mirrors.cernet.edu.cn/FreeBSD-pkg"

umount -Af

rm -Rf /compat/linux
rm -Rf /tmp/archlinux-bootstrap-x86_64.tar.gz
rm -Rf /tmp/root.x86_64

service linux enable
service linux start

fetch -o /tmp "$ARCHLINUX_MIRROR/iso/latest/archlinux-bootstrap-x86_64.tar.gz"
tar -C /tmp -xpf /tmp/archlinux-bootstrap-x86_64.tar.gz || true
cp -Rf /tmp/root.x86_64/* /compat/linux

cat >/compat/linux/etc/pacman.conf <<EOF
[options]
Architecture = auto
ParallelDownloads = 5

[core]
Server = $ARCHLINUX_MIRROR/\$repo/os/\$arch
SigLevel = Required DatabaseOptional

[extra]
Server = $ARCHLINUX_MIRROR/\$repo/os/\$arch
SigLevel = Required DatabaseOptional

[archlinuxcn]
Server = $ARCHLINUXCN_MIRROR/\$arch
SigLevel = Required DatabaseOptional
EOF

chroot /compat/linux pacman-key --init
chroot /compat/linux pacman-key --populate

cp /etc/resolv.conf /compat/linux/etc

chroot /compat/linux pacman --sync --refresh --sysupgrade --noconfirm
chroot /compat/linux pacman --sync --needed --noconfirm archlinuxcn-keyring
chroot /compat/linux pacman --sync --needed --noconfirm code-server

ln -sf /compat/linux/lib/code-server/bin/code-server /usr/local/bin

rm -Rf /compat/linux/home
rm -Rf /compat/linux/root
rm -Rf /compat/linux/usr/local
rm -Rf /compat/linux/usr/src

pkg upgrade -y git bash vim htop tmux llvm bear

code-server --install-extension llvm-vs-code-extensions.vscode-clangd
code-server --install-extension mhutchie.git-graph

rm -Rf /tmp/archlinux-bootstrap-x86_64.tar.gz
rm -Rf /tmp/root.x86_64

欢迎测试与反馈。

最后更新于

FreeBSD 中文社区 2024