VSCode升级18.6后提示“你已连接到不受Visual Studio Code支持的OS版本

发生了什么?

在VSCode最近的一次更新中,提升远程开发使用的node版本,更新后的node依赖glibc>=2.28

在升级1.86版本后,如果尝试连接glibc 2.28以下版本的开发机,会提示连接到不受支持的OS版本。

/posts/vscode%E5%8D%87%E7%BA%A71.86%E5%90%8E%E6%8F%90%E7%A4%BA%E4%BD%A0%E5%B7%B2%E8%BF%9E%E6%8E%A5%E5%88%B0%E4%B8%8D%E5%8F%97visual-studio-code%E6%94%AF%E6%8C%81%E7%9A%84os%E7%89%88%E6%9C%AC/assets/%E8%BF%9E%E6%8E%A5%E5%A4%B1%E8%B4%A5%E6%8F%90%E7%A4%BA.png

按照官方的要求。最好的解决方案自然是升级到更新的glibc版本,这也意味着需要使用更新的系统版本。在社区的热烈讨论下,VSCode项目组最终对于旧glibc延长额外支持12个月。但新版本连接到旧glibc服务器后,仍然会有一行醒目的提示:

/posts/vscode%E5%8D%87%E7%BA%A71.86%E5%90%8E%E6%8F%90%E7%A4%BA%E4%BD%A0%E5%B7%B2%E8%BF%9E%E6%8E%A5%E5%88%B0%E4%B8%8D%E5%8F%97visual-studio-code%E6%94%AF%E6%8C%81%E7%9A%84os%E7%89%88%E6%9C%AC/assets/%E5%A4%B4%E9%83%A8%E8%AD%A6%E5%91%8A.png

本文提供一种极端手动的方法(本来是给开发环境的dockerfile使用),即手动编译一份glibc和node,以专供VSCode的远端服务器使用。

开搞

1. 从编译器说起 (可选)

文档来看,node现在已经要求到了GCC 10,我们的环境很有可能没准备。(如果你的环境已经有一份比较高版本的编译器,可以跳过这里啦)

这里准备了一份贴贴心的安装脚本,以防你真的没有更新的GCC。为了避免搞坏你的环境,它只会在/usr/local/bin下建立gcc-12g++-12,而不会覆盖掉默认的gccg++命令。

# 编译一份GCC, 参考时间半个钟, 配置好的机器请自行按照CPU核心数酌情修改make -jN
curl -OL "https://ftp.gnu.org/gnu/gcc/gcc-12.2.0/gcc-12.2.0.tar.gz" \ 
    && tar -zxvf gcc-12.2.0.tar.gz \ 
    && pushd gcc-12.2.0 \ 
    && ./configure --prefix=/usr/local/gcc-12.2.0 \ 
    && make -j8 \ 
    && make install \ 
    && ln -s /usr/local/gcc-12.2.0/bin/gcc /usr/bin/gcc-12 \ 
    && ln -s /usr/local/gcc-12.2.0/bin/g++ /usr/bin/g++-12 \ 
    && popd \ 
    && rm -r gcc-12.2.0.tar.gz gcc-12.2.0

编译了以后记得把LD_LIBRARY_PATH加上:

export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/gcc-12.2.0/lib64

2. 来一份Glibc

这里我选择编译glibc 2.30,glibc需要更新一些的binutils和make,遂编译之。

按照上文,此处我使用了一份手动编译的,较新版本的gcc编译器,如果你的版本已经足够支持,可以不需要手动指定CCCXX环境变量

因为是从Dockerfile里抠出来的,有一些重复的export,也方便各位节选了🙏

# 编译binutils,参考时间小于5分钟
curl -OL "https://ftp.gnu.org/gnu/binutils/binutils-2.34.tar.gz" \ 
    && tar -zxvf binutils-2.34.tar.gz \ 
    && pushd binutils-2.34 \ 
    && export CC=/usr/bin/gcc-12 \ 
    && export CXX=/usr/bin/g++-12 \ 
    && ./configure --prefix=/usr/local/binutils-2.34 \ 
    && make -j8 \ 
    && make install \ 
    && popd \ 
    && rm -r binutils-2.34 binutils-2.34.tar.gz

# 编译make,参考时间小于5分钟。
curl -OL "https://ftp.gnu.org/gnu/make/make-4.1.tar.gz" \ 
    && tar -zxvf make-4.1.tar.gz \ 
    && pushd make-4.1 \ 
    && export CC=/usr/bin/gcc-12 \ 
    && export CXX=/usr/bin/g++-12 \ 
    && export PATH=/usr/local/binutils-2.34/bin:$PATH \ 
    && ./configure --prefix=/usr/local/make-4.1 \ 
    && make -j8 \ 
    && make install \ 
    && ln -sf /usr/local/make-4.1/bin/make /usr/local/make-4.1/bin/gmake \ 
    && popd \ 
    && rm -r make-4.1 make-4.1.tar.gz

# 编译glibc,参考时间小于10分钟
curl -OL "https://ftp.gnu.org/gnu/glibc/glibc-2.30.tar.gz" \ 
    && tar -zxvf glibc-2.30.tar.gz \ 
    && pushd glibc-2.30 \ 
    && mkdir -p build \ 
    && pushd build \ 
    && export CC=/usr/bin/gcc-12 \ 
    && export CXX=/usr/bin/g++-12 \ 
    && export PATH=/usr/local/binutils-2.34/bin:/usr/local/make-4.1/bin:$PATH \ 
    && export LD_LIBRARY_PATH=/usr/local/gcc-12.2.0/lib64:/usr/local/gcc-12.2.0/lib64 \ 
    && export CXXFLAGS="-g -O2 -Wno-array-parameter" \ 
    && export CFLAGS="-g -O2 -Wno-array-parameter" \ 
    && ../configure --prefix=/usr/local/glibc-2.30 --disable-werror \ 
    && make -j8 \ 
    && make install \ 
    && popd \ 
    && popd \ 
    && rm -r glibc-2.30 glibc-2.30.tar.gz

3. 来一份node

这里选择了v18.19,直接开编吧。

# 编译node,参考时间20分钟
git clone --depth=1 -b v18.19.0 https://github.com/nodejs/node.git \ 
    && pushd node \ 
    && export CC=/usr/bin/gcc-12 \ 
    && export CXX=/usr/bin/g++-12 \ 
    && export PATH=/usr/local/binutils-2.34/bin:/usr/local/make-4.1/bin:$PATH \ 
    && ./configure --prefix=/usr/local/node-18.19 \ 
    && make -j8 \ 
    && make install \ 
    && popd \ 
    && rm -r node

4. patch一下

# 以防你真的没有patchelf
yum install patchelf
# 这里不同环境具体文件名可能不同,但路径基本一致,请按实际情况修改
patchelf --set-interpreter /usr/local/glibc-2.30/lib/ld-linux-x86-64.so.2 \ 
         --set-rpath /usr/local/glibc-2.30/lib/:/lib/x86_64-redhat-linux6E/:/usr/lib/x86_64-redhat-linux6E \ 
         /usr/local/node-18.19/bin/node

5. 替换到VSCode

~/.vscode-server/bin/{id}中存储了VSCode自带的node实例,这里简单替换下,可以将下面的内容保存到.sh脚本运行(记得替换成自己的路径)。

#!/bin/sh
# 记得替换成自己的路径
binary_base="/root/.vscode-server/bin/903b1e9d8990623e3d7da1df3d33db3e42d80eda"
replace_base="/usr/local/node-18.19"

mv "$binary_base/node" "$binary_base/node.bak"
ln -sf "$replace_base/bin/node" "$binary_base/node"

接着,kill掉VSCode所在的进程,或直接重启机器,再重新连接就完成了。

或者使用下面这个脚本,一键替换机器上所有VSCode实例:

#!/bin/sh
# upgrade vscode's node binary to v18
function _internal_replace_binary() {
    binary_base="$1"
    replace_base="$2"
    mv "$binary_base/node" "$binary_base/node.bak"
    ln -sf "$replace_base/bin/node" "$binary_base/node"
    echo "✅ 升级成功: $binary_base"
}

function _internal_replace_all_vscode_server() {
    replace_base="/usr/local/node-18.19"
    if [ ! -f "$replace_base/bin/node" ]; then
        echo "❎: 镜像文件缺失 ($replace_base/bin/node)"
        return 1
    fi
    for dir in $(find ~/.vscode-server/bin -maxdepth 1 -mindepth 1 -type d)
    do
        _internal_replace_binary $dir $replace_base
    done
    echo "✅ 全部完成: 请重连 VSCode 或重启设备"
    ps -ef | grep "\.vscode-server/bin" | grep node | awk '{print($2)}' | xargs -i kill {}
}

_internal_replace_all_vscode_server