为 Rust+QT 编程搭建【伪】win32 开发环境

为 Rust+QT 编程搭建【伪】win32 开发环境

为Rust+QT编程搭建【伪】win32开发环境

细心的读者一定会注意到文章标题内的【伪】字。哎!实属无奈。Rust Qt Binding对win32编译环境实在不友善。在windows操作系统上,qmetaobject始终编译失败 --- 这是一个已备案的缺陷。幸运的是,我本地操作系统是Windows 10 x64,所以还有一条“曲线救国”的“狗血”技术路线。概括地讲,

在Windows 10 x64上,安装Ubuntu-20.04子系统(绝不是VM,而是WSL2)。即是传说中的Windows Subsystem for Linux 2。

在Ubuntu-20.04子系统内,安装全套的

rustup工具链

Qt SDK

题外话,即便安装一个nwjs for Linux,其也是能够正常运行的。

将Windows 10 x64上的VSCode远程连接至Ubuntu-20.04。这样一来,

既将VSCode人机交互界面继续保留在Windows端 --- 开发体验不降级。

又将rustc编译与Qt link都迁移至Linux端执行 --- 绕开qmetaobject对windows编译环境的不兼容。

将Ubuntu-20.04的图形界面投影至Windows 10 x64。以便,能够在windows 10环境上,观察GUI程序的修改效果 --- 这还是为保持开发体验不降级。

在功能开发结束之后,在target/release和target/debug目录下的就是Linux版的GUI应用程序分发包。

需要借助【交叉编译】,才能在target/x86_64-pc-windows-msvc文件夹内,获得Windows版的GUI应用程序分发包。

下面就是这一出既“狗血”又超级麻烦的自虐之旅的详细内容。希望世界变得更平,别让我这样的Windows狗活得那么辛苦。

为什么选择QT?

前不久,我写的另一篇文章为Rust原生gui编程,搭建win32开发环境分享了如何将Rust链接Gnome.GTK3实现GUI应用程序开发。对照两款图形界面解决方案,Gnome.GTK3上手容易,文档齐备,社区活跃。但是,QT强烈吸引我的有两点:

覆盖全平台。

从【桌面】,到【移动端】,再到【嵌入式设备】

从Windows,到Linux与Mac,再到Android与iOS

虽然眼下Rust Qt Binding对windows操作系统的兼容性还有不足,但我相信这仅只是临时缺陷,会被尽快修复。

可移植性强。

至少它依赖的动态链接库里没有与win32内置dll重名的。Gnome.GTK3的这个毛病可是把我给恶心到了。我之前可是下了佬大的时间与精力才定位出此crash的原因。

我甚至有一个不成熟的想法:“QT才是前端开发者最终的技术归宿”,因为它:

全平台

高性能

亲和IoT --- 还是有相当多的IoT设备是拥有独立屏显的。不可能在所有的IoT硬件上都运行一个web service等着别人用浏览器访问(这不是高端玩家的作法)。

不过在这里,我还是想分别对Gnome.GTK3与QT分别吐槽三点:

Gnome.GTK3对·新人上手·真是没得说、太棒了。但是,其【高级组件】(比如,webview)不支持windows平台太挫伤开发者的深度使用热情了。

QT几乎无所不能。但是,Rust Qt Binding对win32编译环境的适用性太差。这对普通玩家的入手门槛有些高了。

最后,上面两个问题都不是新问题,而都是陈年老梗了。官方怎么对这些缺陷的解决这么不上心呀?

为什么选择qmetaobject?

qmetaobject是QT官方团队维护的项目。我相信其后续迭代有保障。QT官宣软文看这里。

你不会以为其它的Rust Qt Binding解决方案对win32编译环境友善吧?Naively! 事实上,以下几款Rust Qt Binding开源项目,我都试过了。它们中没一个对win32友好的。此时,我脸上只有一个表情就是“悲愤”。世界一点儿也不平!

ritual

qmlrs

qml-rust

劝退理由

整个开发环境包括子系统Ubuntu-20.04 (WSL)和宿主Windows 10 x64两个部分。其对开发环境的软件与硬件条件都有一点儿要求:

低版本的Windows操作系统不具备WSL2功能。所以,如果你的操作系统是Windows 7,推荐先研究一下如何免费地升级到Windows 10。

从微软应用程序市场下载安装的Ubuntu-20.04 (WSL2)仅只是一个Linux Kernel(大约1.2GB)。所以,后续需要安装的软件包非常地多。你的系统盘足够大吗?若系统盘的存储余额小于25GB,推荐先研究一下【系统盘】“搬家”以腾出足够的空间折腾。

开发环境搭建概述

被用来验证开发环境的Rust + QT工程是来自官方的演示例程todos

整个安装配置大约是以下若干步:

安装与配置Ubuntu-20.04 (WSL2)

配置宿主端Windows 10

回到Ubuntu-20.04 (WSL2)安装Qt SDK。因为QT的连线安装程序是GUI的,所以必须先在第二步解锁【从WSL2子系统向宿主系统投影图形界面】的技能。

从Ubuntu-20.04端,使用VSCode打开todos工程。

从VSCode集成终端,执行cargo run弹出应用程序窗口,验证开发环境正常工作。

开发环境搭建之子系统端 - Ubuntu-20.04 (WSL2)

安装Ubuntu 20.04 (WSL2)子系统

我给WSL2子系统安装Ubuntu不是因为Ubuntu有什么独一无二的技术优势;而仅是,相对于CentOS,Ubuntu 20.04子系统不要钱。在微软应用程序商店,除了Mac以外(知识产权保护)的桌面系统都有WSL2镜像安装包。它们之间最大的差别就是【收费】与【免费】。

再次劝退

Windows Subsystem for Linux 2会被强制安装于系统C盘。所以,在安装Ubuntu 20.04 (WSL2)子系统之前,请先确认你的系统盘是否还有足够的剩余空间。在我的使用场景里,子系统满载体量大约14GB(以后,可能还会更大)。虽然WSL2既好看又好用,但它是有成本的。

Ubuntu 20.04 (WSL2)子系统与Windows 10 x64宿主系统之间的关系

硬盘关系

子系统的硬盘对宿主系统是不可见。

宿主系统的硬盘是被逐个mounted到Ubuntu 20.04 (WSL2)【挂载点】/mnt虚拟文件夹下,所以宿主系统文件系统对子系统皆是可见的,且被视作外部存储设备。

环境变量关系

子系统会继承(甚至,重写)宿主系统的全部环境变量 --- 这个潜在地造成了混乱,我后面会给出规避方式。

宿主系统访问不到子系统内的环境变量。

程序执行关系

子系统可运行宿主系统的.exe文件。比如,ipconfig.exe --- 后面会用到。

宿主系统既看不到,也执行不了子系统的可执行文件。

安装步骤

以【管理员】身份运行PowerShell

激活Windows Subsystem for Linux 2功能

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

下载与安装Linux Kernel 更新包

将WSL2设置成为默认启动项

wsl --set-default-version 2

从【微软应用程序商店】搜索Ubuntu 20.04 LTS并安装之。除了Ubuntu外,还提供有CentOS子系统。但,这是收费的。

WSL2子系统基本操作

开机

wsl -d Ubuntu-20.04

在第一次启动Ubuntu 20.04 LTS子系统时,你会被要求创建一个管理员账号。根据提示,你照做就是了。

关机

wsl --shutdown

# 或

wsl -t Ubuntu-20.04

打开运行中子系统的终端

wsl

列出所有已安装的子系统

wsl -l -v

上面所有操作在cmd和PowerShell均可完成。但,我还是推荐安装windows terminal。

配置Ubuntu 20.04 (WSL2)子系统

启动Ubuntu 20.04 (WSL2)子系统

打开cmd或PowerShell终端,执行

wsl -d Ubuntu-20.04

在第一次启动Ubuntu 20.04 LTS子系统时,你会被要求创建一个管理员账号。根据提示,你照做就是了。

软件包安装

sudo apt-get install -y build-essential libfontconfig1 mesa-common-dev libglu1-mesa-dev libxkbcommon-x11-0 libwayland-cursor0 net-tools curl libxcb-icccm4 libxcb-image0 libxcb-shm0-dev libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcursor1 mingw-w64 desktop-file-utils libnss3-tools libcups2-dev libgconf-2-4 libpangocairo-1.0-0 libxss1 libatk1.0-0 libgtk-3-dev fonts-arphic-ukai fonts-arphic-uming language-pack-zh* chinese* ruby ruby-dev

上面这些软件包涉及了:

gcc编译工具链

代码编辑工具

版本控制工具

图形界面基础库

交叉编译(面向windows操作系统)

中文字符集

等等吧。可谓是一应俱全。也足以支持nwjs for Linux正常运行。

配置vi编辑器

虽然VSCode能够直接编辑Ubuntu 20.04 (WSL2)子系统内任何文本文件,但是vi仍旧是一款很有仪式感且使用便捷的文本编辑器。

vi ~/.vimrc

# 添加如下内容

set textwidth=200

set shiftwidth=2

set tabstop=2

set expandtab

set nu

set autoindent

set cindent

set fileencodings=utf-8

set termencoding=utf-8

set encoding=prc

# 并保存退出

重置环境变量

默认情况下,WSL2子系统会继承宿主操作系统的环境变量。我遇到多次因为Ubuntu 20.04 (WSL2)少装了软件包,而误载入了同名的Windows 10宿主动态链接库的情况。这类问题一旦出现很难定位原因,错误日志的内容也很让人费解。解决起来好是耽误功夫!于是,我才总结出这么一条来。将它们“切隔”得泾渭分明。

vi ~/.bashrc

# 在文件的最顶端,添加

export PATH="/usr/local/sbin:/usr/local/bin";

export PATH="$PATH:/usr/sbin:/usr/bin";

export PATH="$PATH:/sbin:/bin";

export PATH="$PATH:/usr/games:/usr/local/games";

export PATH="$PATH:/snap/bin";

# 保存文件和退出文件

source ~/.bashrc

# 给宿主端的 VSCode 启动脚本创建符号链接。

# - 你本地的 VSCode 安装目录,可能有所不同,注意替换。

sudo ln -s '/mnt/c/Program Files/Microsoft VS Code/bin/code' code

另一方面,倘若你真的有必要在WSL2子系统里执行宿主系统的.exe文件(比如,执行ipconfig.exe来获得宿主系统的ip地址):

要么,直接写绝对地址

要么,在/snap/bin目录下,创建符号链接

sudo ln -s /mnt/c/Windows/System32/ipconfig.exe /snap/bin/ipconfig.exe

千万不要改PATH环境变量,因为PATH仅只对目录有效,一次至少会导入一个文件夹的动态链接库,简直是后患无穷。在本次开发环境搭建过程中,涉及到的符号链接只有两个:

ipconfig.exe - 下文会提到使用它来实时地获取宿主操作系统的ip地址。

VSCode的启动脚本文件${VSCode_安装目录}/bin/code - 以便从子系统端打开一个工程和编辑代码。

安装node.js

先安装nvm版本管理器,再安装node,npm与pnpm。

提示:安装脚本可能需要“科学上网”才能被下载。所以,export http_proxy=与export https_proxy=两个环境变量没准需要你配置好,以指向某个有效的地址。

curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash

source ~/.bashrc

nvm install 10.24.1

nvm use 10.24.1

nvm alias default v10.24.1

npm i -g pnpm

安装ruby依赖包

sudo gem install compass

sudo gem install sass

安装rustup toolchain

提示:工具链模块可能需要“科学上网”才能被下载。所以,export http_proxy=与export https_proxy=两个环境变量没准需要你配置好,以指向某个有效的地址。

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

source $HOME/.cargo/env

# `nightly`频道最新版本的`rustup toolchain`不兼容于`Rust Qt Binding`,所以再安装老一点的版本。

rustup toolchain install nightly-2021-03-25-x86_64-unknown-linux-gnu

# 这一步理论上是可选的,因为`cargo`工程能够在工程层级重写`rustup toolchain`的版本指向。

rustup default nightly-2021-03-25-x86_64-unknown-linux-gnu

# 向 windows 10 x64 交叉编译时,用得上。

rustup target add x86_64-pc-windows-msvc

vi ~/.bashrc

# 在文件的最尾端,添加

# - 调试程序时,可看得见完整的调用栈

export RUST_BACKTRACE=1;

# 保存文件和退出文件

source ~/.bashrc

到写这篇文章时止,qmetaobject与最新版的nightly-x86_64-unknown-linux-gnu工具链还有兼容性问题(编译报错),所以我本地安装的nightly rustup toolchain还是2021年3月的版本。借助我所做的另一款开源工具rustup-components-history,你能找到更多的nightly rustup toolchain的历史版本。毕竟,官方给出的历史版本可用性清单仅提供最新7天的信息。

大家看,我的相关功课做得全吧?是不是,无处不惊喜?所以,请帮忙,多转发,多参阅。

配置XSTATA向宿主系统投影图形界面

题外话,XSTATA与X11 Server是个啥子关系?

虽然官方文档与名称暗示都是将X11 Server摆在了【后端】的位置上,但真实情况正好是对调的。X11 Server负责收集使用者的键盘与鼠标输入,像是个网页;而XSTATA居中调度,完成图形计算与给出反馈,像是个web service。

在这段配置里有一个痛点。即,WSL2子系统需要明确地知道X11 Server所在的Windows 10宿主操作系统的ip地址。请不要自做聪明地认为127.0.0.1可能搪塞过去。127.0.0.1是指向Ubuntu 20.04 (WSL2)自身的 --- 咱们一定得把WSL2子系统与宿主看作是两个独立的workstation。若你的电脑正在连接一个DHCP路由器,那么情况会更糟一些,因为电脑的ip地址会经常地变化。而反复地修改Linux配置文件往往是各种坏事情的开端。

为了缓解这个痛点,我采取的措施包括两步:

在/snap/bin目录下,给C:\Windows\System32\ipconfig.exe创建一个符号连接/snap/bin/ipconfig.exe

sudo ln -s /mnt/c/Windows/System32/ipconfig.exe /snap/bin/ipconfig.exe

在$HOME目录下,编写了一个.get_hot_ip.js脚本程序,来

执行/snap/bin/ipconfig.exe命令

解析/snap/bin/ipconfig.exe在标准输出打印的文本内容

从输出内容里扣出宿主系统的ip地址 --- 需要一点儿简单的正则匹配

打印此ip地址字符串至标准输出

然后,就可以简单地修改~/.bashrc文件了:

vi ~/.bashrc

# 在文件的最尾端,添加

export LIBGL_ALWAYS_INDIRECT=;

# 执行 js 文件获取 ip 地址

export DISPLAY="$(~/.get_hot_ip.js):0.0";

# 保存文件并退出

source ~/.bashrc

于是,即便DHCP路由器时不时地改变了我们的ip地址,咱们只要source ~/.bashrc一下,新ip地址就生效了。多亏了有nodejs,要是用shell来写这个字符串解析程序,那得是多费劲呀。

配置显示中文(包括命令行与GUI)

sudo locale-gen zh_CN.utf8

sudo update-locale LANG=zh_CN.utf8

然后,关闭当前终端和打开一个新终端,语言切换便随之生效了。另外,敲入命令locale也能查阅当前的【语言本地化】配置。

开发环境搭建之Windows宿主系统端

安装X-Server for WinNT

这是将Ubuntu 20.04 (WSL2)子系统的图形界面投影至Windows 10宿主系统的关键组件。虽然它的名字叫X11 Server,但就它的功能而言,其更像是B/S架构中的网页端。与X-Server呼应的、在Ubuntu 20.04 (WSL2)子系统内的Client端程序是XSTATA。

下载与安装VcXsrv

设置【高DPI显示模式】

打开VcXsrv安装目录

鼠标右键点击xlaunch.exe文件。

点击【属性】菜单项

在【属性】对话框内,切签至【兼容性】选项卡

点击【更高DPI设置】按钮

在新对话框内,选中【替代高DPI缩放行为】复选框。

在【缩放执行】下拉框内选择【系统(增强)】

最后,一路【确定】下来。

启动X-Server for WinNT

生成启动配置文件

双击VcXsrv安装目录(下文记作:%VCXSRV_HOME%)内的XLaunch.exe文件

打开Display Settings配置窗口

选择Multiple Windows(就是左一项)

在Display number数字录入框内,输入0

点击【下一页】按钮

跳转至Client startup配置窗口

选择Start no Client

点击【下一页】按钮

跳转至Extra Settings配置窗口

选中Clipboard

选中Primary Selection

取消选中Native opengl

选中Disable access control

点击【下一页】按钮

跳转至Finish configuration配置窗口

点击按钮save configuration,保存配置为文件%VCXSRV_HOME%\x11-server.xlaunch

点击【取消】按钮。

将配置文件应用于快捷方式

鼠标右(键)击XLaunch快捷方式

在【属性】对话框中,修改【目标】输入框的内容为

"%VCXSRV_HOME%\xlaunch.exe" -run "%VCXSRV_HOME%\x11-server.xlaunch"

点击【确定】按钮

启动X11 Server

双击新XLaunch快捷方式

配置VSCode

安装插件

Remote - WSL

Remote Development

在【用户配置】文件%AppData%\Code\User\settings.json里,添加一个新配置项"http.proxySupport": "off"。否则,在WSL2模式下,安装扩展插件容易遭遇下载网络失败。

关闭当前VSCode实例

从Ubuntu-20.04(WSL)终端,敲入指令code .。在WSL2模式下,启动另一个VSCode实例。

给运行于WSL2模式下的VSCode实例安装三个rust插件

rust-analyzer

vscode-rust-syntax

CodeLLDB

给Ubuntu-20.04 (WSL2)子系统安装Qt-5.12.11

准备工作

在Windows 10 x64宿主系统,双击XLaunch快捷方式启动X11 Server服务,准备接受来自WSL2子系统的图形界面投影。

在Ubuntu-20.04终端命令行,执行source ~/.bashrc确保之前的配置修改皆奏效。

安装框架与组件库

打开Ubuntu-20.04 (WSL2)命令行终端

wsl

在Ubuntu-20.04 (WSL2)命令行终端,执行如下命令,下载与启动【Qt连线安装器】

wget https://download.qt.io/official_releases/online_installers/qt-unified-linux-x64-online.run

chmod +x qt-unified-linux-x64-online.run

./qt-unified-linux-x64-online.run

于是,在Windows 10 x64宿主端,Qt安装向导对话框就会被弹出。此时,若完全安装Qt-5.12.11框架和组件库,则需要几十个GB的空间。我推荐:

只安装Desktop gcc 64-bit与Qt WebEngine(webview这是刚需)就足够了。

选择安装目录为$HOME/Qt。

配置环境变量

vi ~/.bashrc

# 在文件的最尾端,添加

export PATH="$HOME/Qt/5.12.11/gcc_64/bin:$PATH";

export LD_LIBRARY_PATH="$HOME/Qt/5.12.11/gcc_64/lib:$LD_LIBRARY_PATH";

# 保存文件并退出

source ~/.bashrc

验证Qt安装成功

执行如下脚本,启动Qt的图形界面开发工具Qt Creator。

~/Qt/Tools/QtCreator/bin/qtcreator.sh

验证Ubuntu-20.04 (WSL2)开发环境

检查桌面右下角的托盘,确认X11 Server是否已经启动。

克隆工程从github至本地git clone https://github.com/woboq/qmetaobject-rs.git。

这一步发生在宿主系统或Ubuntu-20.04 (WSL2)子系统不重要。

在WSL2命令行终端,cd到工程根目录/examples/todos

输入指令code .,回车。以WSL2模式,启动一个VSCode实例。

打开VSCode集成终端,敲入cargo run命令,开始编译/运行程序。

另一方面,若你本地已经配置好了CodeLLDB插件与编写了正确.vscode/lanuch.json,直接F5开始断点调试就更酷了。

如果一切正常的话,此时此刻

VSCode代码编辑器是在宿主环境Windows 10 x64。请继续你之前的编程习惯。是不是很舒服?

cargo run使用的是Ubuntu-20.04(WSL)子系统的gcc工具链

甚至,包括更高级的F5断点调试也都是走的Ubuntu-20.04(WSL)子系统gcc工具链

todos图形窗口则是从Ubuntu-20.04(WSL),透过XSTATA -> X11 Server,被投影到宿主Windows 10 x64操作系统的。

交叉编译

就目前的Windows编译工具链现状而言,若想支持向Windows平台分发应用程序软件包,这一步还是绕不过去的。我相信这个情况后续一定会有改善的。这个事已经在github上向官方技术团队备案了,且已经被受理,和正在处理中。

首先,要明确【rust交叉编译】是有限制的。即,rustup工具链的nightly频道对【交叉编译】都不可用。我们仅能使用stable rustup工具链。在这,你是不是也感觉心拔凉拔凉的?

然后,咱们再讲怎么搞【交叉编译】。

在上文中的【rustup toolchain安装】章节,咱们已经预安装了x86_64-pc-windows-msvc``target。

此target可不认识nightly rust语法,会编译报错的。

除此之外,我们还要准备一套Qt for Windows。

直接在你的宿主端windows 10 x64上,下载与运行Qt for Windows 连线安装向导

勾选安装Qt 5.12.11 -> MSVC 2017 64-bit。 注意:我勾选安装MSVC 2017 64-bit是因为我本地已经有Visual Studio 2017了。 请先确定你自己电脑的预装了哪一版的Visual Studio再合理地选择Qt MSVC类型。

接着,仅交叉编译时,临时配置环境变量QT_INCLUDE_PATH与QT_LIBRARY_PATH。

前者,export QT_INCLUDE_PATH=/mnt/${QT_WINDOWS_安装目录}/5.12.11/msvc2017_64/bin

后者,export QT_LIBRARY_PATH=/mnt/${QT_WINDOWS_安装目录}/5.12.11/msvc2017_64/lib

因为QT_INCLUDE_PATH与QT_LIBRARY_PATH优先级高于PATH与LD_LIBRARY_PATH,所以后续的【交叉编译】会优先链接QT for Windows,而不是开发时依赖的QT for Linux。

最后,cd到工程根目录/examples/todos和执行命令cargo build --target=x86_64-pc-windows-msvc。完成。

rust自身的交叉编译就是这么简单得,不要,不要的。复杂度都集中在它所链接的cpp链接库上了。

增补篇:将整个Ubuntu-20.04(WSL)桌面投影至Windows 10宿主系统

更多软件包安装

sudo apt-get install -y ubuntu-desktop apt-transport-https systemd-genie unzip imagemagick

允许当前账号·免密·运行systemd-genie

在编辑sudoer过程中,请将下文中的account_name替换成你的真实账号名(即,echo $USER的值)。

sudo visudo --file /etc/sudoers.d/$USER

# 添加如下一行内容

account_name ALL=(ALL) NOPASSWD:/usr/bin/genie

# 保存与退出:`Ctrl + o`,`Ctrl + x`。

若你想图省事,不防试试下面这条一步到位的指令。不过,这就少了一次熟悉与练习visudo编辑器用法的机会。

echo "$USER ALL=(ALL) NOPASSWD:/usr/bin/genie" | sudo EDITOR="tee" visudo --file /etc/sudoers.d/$USER

若是不小心把sudoer给改坏了,也不用着急。

首先,从win32命令行执行wsl -u root以超级用户登录Ubuntu-20.04(WSL)

然后,强制编辑/etc/sudoers.d/$USER文件,纠正错误配置记录。

千万用不着,为此,重装整个Linux子系统 --- 这算不上一次事故。

向apt-get源清单添加Microsoft站点

# 进入管理员模式

sudo --shell

# 安装 Microsoft 站点公钥

apt-key adv --fetch-keys https://packages.microsoft.com/keys/microsoft.asc

# 添加 Microsoft 地址

vi /etc/apt/sources.list.d/microsoft-prod.list

# 添加如下一行内容

deb [arch=amd64] https://packages.microsoft.com/ubuntu/20.04/prod focal main

# 保存与退出

apt update

exit

向apt-get源清单添加Arkane站点

# 进入管理员模式

sudo --shell

# 安装 GPG 公钥

wget --output-document /etc/apt/trusted.gpg.d/wsl-transdebian.gpg https://arkane-systems.github.io/wsl-transdebian/apt/wsl-transdebian.gpg

chmod a+r /etc/apt/trusted.gpg.d/wsl-transdebian.gpg

# 添加 Arkane 地址

vi /etc/apt/sources.list.d/wsl-transdebian.list

# 添加如下两行内容

deb https://arkane-systems.github.io/wsl-transdebian/apt/ focal main

deb-src https://arkane-systems.github.io/wsl-transdebian/apt/ focal main

# 保存与退出

apt update

exit

编写启动脚本

创建启动脚本保存目录

在windows 10的%USERPROFILE%目录下,创建文件夹.ubuntu来保存Ubuntu-20.04的桌面系统启动脚本。这样方便从宿主环境创建快捷方式和“一站式”地启动Ubuntu-20.04(WSL)桌面。

export USERNAME=$(wslvar USERNAME);

export USERPROFILE=/mnt/c/users/$USERNAME;

mkdir --parents $USERPROFILE/.ubuntu/

cd $USERPROFILE/.ubuntu/

编写GNOME.GTK3启动shell脚本

vi start_ubuntu.sh

# 添加如下内容

#!/usr/bin/env bash

# 显示环境变量,用于连接 X11 Server

if [ -z "$DISPLAY" ]; then

# 初始化 NVM 与 NODE 环境

export NVM_DIR="$HOME/.nvm"

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"

# 执行 nodejs 脚本程序获取宿主系统的 ip 地址。其在前文已经提过。

export DISPLAY="$(~/.get_hot_ip.js):0.0"

fi

# GNOME.GTK3 环境变量

export DESKTOP_SESSION="ubuntu"

export GDMSESSION="ubuntu"

export XDG_SESSION_DESKTOP="ubuntu"

export XDG_CURRENT_DESKTOP="ubuntu:GNOME"

export XDG_SESSION_TYPE="x11"

export XDG_BACKEND="x11"

export XDG_SESSION_CLASS="user"

export XDG_DATA_DIRS="/usr/local/share/:/usr/share/:/var/lib/snapd/desktop"

export XDG_CONFIG_DIRS="/etc/xdg"

export XDG_RUNTIME_DIR="$HOME/xdg"

export XDG_CONFIG_HOME="$HOME/.config"

export XDG_DATA_HOME="$HOME/.local/share"

export XDG_CACHE_HOME="$HOME/.cache"

export XDG_DESKTOP_DIR="$HOME/Desktop"

export XDG_DOCUMENTS_DIR="$HOME/Documents"

export XDG_DOWNLOAD_DIR="$HOME/Downloads"

export XDG_MUSIC_DIR="$HOME/Music"

export XDG_PICTURES_DIR="$HOME/Pictures"

export XDG_PUBLICSHARE_DIR="$HOME/Public"

export XDG_TEMPLATES_DIR="$HOME/Templates"

export XDG_VIDEOS_DIR="$HOME/Videos"

# 启动 GNOME.GTK3 桌面环境

gnome-session

保存并退出

编写wsl2启动javascript脚本

因为有一段比较麻烦的“重试”逻辑需要被实现,所以我选择了js --- 既简单,又强大。需实现的业务逻辑包括:

执行wsl genie -c /mnt/c/Users/${process.env.USERNAME}/.ubuntu/start_ubuntu.sh指令

跟踪标准输出内容,判断其是否包含字符串Timed out waiting for systemd to enter running state.?

若在标准输出内包含Timed out waiting for systemd to enter running state.,就杀进程和回到#1。

否则结束。

vi start_ubuntu.js

# 添加如下内容

#!/usr/bin/env node

const {spawn} = require('child_process');

(async () => {

const {stdout} = await exec('wsl', ['genie', '--is-in-bottle']).catch(output => output);

const shellPath = `/mnt/c/Users/${process.env.USERNAME}/.ubuntu/start_ubuntu.sh`;

if (/inside/.test(stdout)) {

await exec(shellPath);

} else {

const run = () => {

return exec('wsl', ['genie', '-c', shellPath], (_, output) => {

if (/Timed out waiting for systemd to enter running state\./.test(output.stdout)) {

output.prc.kill();

}

}).catch(err => {

if (/Timed out waiting for systemd to enter running state\./.test(err.stdout)) {

return run();

}

return Promise.reject(err);

});

}

await run();

}

})();

function exec(cmd, args = [], notify = () => {}){

return new Promise((resolve, reject) => {

const prc = spawn(cmd, args, {shell: true});

const output = {

prc,

stdout: '',

stderr: ''

};

prc.stdout.on('data', data => {

output.stdout += data.toString();

notify('stdout', output);

});

prc.stderr.on('data', data => {

output.stderr += data.toString();

notify('stderr', output);

});

prc.on('close', code => {

if (code == 0) {

resolve(output);

} else {

reject(output);

}

});

});

}

编写VcXsrv启动配置xml文件

vi ubuntu-desktop-wsl2-preset.xlaunch

# 添加如下内容

LocalProgram="xcalc" RemoteProgram="xterm" RemotePassword="" PrivateKey=""

RemoteHost="" RemoteUser="" XDMCPHost="" XDMCPBroadcast="False" XDMCPIndirect="False"

Clipboard="True" ClipboardPrimary="True" ExtraParams="" Wgl="False" DisableAC="True"

XDMCPTerminate="False"/>

保存并退出

编写VcXsrv重启powershell脚本

vi restart_vcxsrv.ps1

# 添加如下内容

# 终止正监听于 0.0 显示码的 X11 Server 实例

get-process vcxsrv | where { $_.mainwindowtitle -like "*0.0*" } | stop-process

# 启动监听于 0.0 显示码的 X11 Server 实例

start-process "C:\Program Files\VcXsrv\xlaunch.exe" -argument "-run `"$env:USERPROFILE\.ubuntu\ubuntu-desktop-wsl2-preset.xlaunch`""

编写Ubuntu-20.04(WSL)桌面启动引导VBS程序

在此步骤,使用VBS而不是javascript。其主要原因是:由VBS唤起的【命令行窗口】会自动隐藏起来,而由nodejs唤起的命令行窗口就一直在那儿,不能被收起了。若强制收起,则整个进程就结束了。这虽然不影响功能,但太膈应了。

vi start_ubuntu.vbs

# 添加如下内容

set shell_object = createobject("wscript.shell")

userprofile = shell_object.ExpandEnvironmentStrings("%USERPROFILE%")

' 首先,启动 X11 Server

set application = createobject("shell.application")

application.shellexecute "powershell", "-file " & userprofile & "\.ubuntu\restart_vcxsrv.ps1", "", "", 0

' 给 X11 Server 启动,留一点时间

wscript.sleep 1000

' 再启动 wsl2

shell_object.run "node " & userprofile & "\.ubuntu\start_ubuntu.js", 0

制作Ubuntu-20.04(WSL)图标

# 下载 Ubuntu 图标与图片集

wget https://assets.ubuntu.com/v1/9fbc8a44-circle-of-friends-web.zip

# 解压缩之

unzip 9fbc8a44-circle-of-friends-web.zip

# 重新设定 Logo 图片大小,文件类型与文件名

convert -resize 64x64 ./circle-of-friends-web/png/cof_orange_hex.png ubuntu.ico

# 删除无用文件

rm -f 9fbc8a44-circle-of-friends-web.zip

rm -fr 9fbc8a44-circle-of-friends-web

为Ubuntu-20.04(WSL)桌面启动,创建快捷方式

在PowerShell中,执行如下指令

# Define location variables

$shortcut_location = "$env:userprofile\.ubuntu\Ubuntu.lnk"

$program_location = "$env:userprofile\.ubuntu\start_ubuntu.vbs"

$icon_location = "$env:userprofile\.ubuntu\ubuntu.ico"

# Create shortcut

$object = new-object -comobject wscript.shell

$shortcut = $object.createshortcut($shortcut_location)

$shortcut.targetpath = $program_location

$shortcut.iconlocation = $icon_location

$shortcut.save()

便会在%USERPROFILE%\.ubuntu目录下,看到一个Ubuntu快捷方式。双击之就会

先启动X11 Server

再启动WSL2

接着,初始化GNOME.GTK3会话

显示Ubuntu桌面

收尾工作

双击Ubuntu快捷方式,启动Ubuntu桌面

从Activaties打开Terminal终端

执行如下命令

# 关闭【屏幕锁】功能。一旦被锁,非重启不能解锁。实在太恶心了。

gsettings set org.gnome.desktop.lockdown disable-lock-screen true

# 安装应用商店

sudo snap install snap-store

结束

最初,我仅只想解决Rust + QT在win32平台上的编译问题。谁知经历了一番探索与实践却在Windows 10平台内搞出一套完整的Linux子系统来。这似乎又范了我做事不够专注的老毛病了。得改,得改!但是,我真心希望这里分享的内容能够帮助到技术同路人们。搞技术不容易,大家多分享,多相互帮助,共同进步。

相关推荐

提示信息
365bet备用在线

提示信息

📅 07-04 👀 2227
【掊克】什么意思
365bet备用在线

【掊克】什么意思

📅 08-14 👀 1431
Root已死?
www.bet3365

Root已死?

📅 07-04 👀 6292
王者荣耀为什么总匹配到垃圾队友
www.bet3365

王者荣耀为什么总匹配到垃圾队友

📅 08-18 👀 8489