萌新的 linux qt 程序编译实战

事情的来源,是两个月前和朋友讨论啥软件界面又好看「学术网站」又快界面又漂亮。clash 虽然漂亮,但支持的协议少了点。

这时候我就提了一嘴「qv2ray 新界面呀,不是挺漂亮的吗,只是要编译,又不麻烦」

前半句是对的,确实挺漂亮的:

但为了圆后半句,我花了两个月时间。。。不要小看编译啊 kora!

环境:ubuntu 20.04 桌面端,否则没有 opengl 支持,没法试还报错

标准编译步骤——借助 ubuntu 内置的 apt 安装依赖并直接编译

初步安装依赖

1
2
3
4
5
6
sudo apt update
sudo apt install git openssl -y
#编译所需工具
sudo apt install autoconf automake libtool build-essential -y
# qv2ray 和 grpc 编译所需的 ssl 库
sudo apt install libssl-dev -y

安装 qt5(通过官方在线安装包)

参考文章:How do I move ~/Qt to /usr/local/share/Qt? | Qt Forum

需要保持网络通畅!

下载:

1
2
3
wget http://qt.mirror.constant.com/archive/online_installers/4.1/qt-unified-linux-x86_64-4.1.0-online.run
chmod +x ./qt-unified-linux-x86_64-4.1.0-online.run
./qt-unified-linux-x86_64-4.1.0-online.run

根据提示,注册并安装 qt 5.12.5 。我就直接装在默认路径,/home/zbttl/xxx 下了。另外程序默认会安装 Ninja 和 cmake,这两个后面都会用得上。

将 qt 和 cmake 路径加到 root 和在进行操作的用户的环境变量中(后续如果不装到系统目录中,也可以省去吧路径加到的 root 下的命令,即第一条命令)

1
2
3
4
echo "export PATH=/home/zbttl/Qt/5.15.2/gcc_64/bin:/home/zbttl/Qt/Tools/CMake/bin/:/home/zbttl/Qt/Tools/Ninja/:$PATH"|sudo tee /etc/bash.bashrc

echo "export PATH=\$HOME/Qt/5.15.2/gcc_64/bin:\$HOME/Qt/Tools/CMake/bin/:\$HOME/Qt/Tools/Ninja/:\$PATH" >> ~/.bashrc
source ~/.bashrc

测试:

1
2
cmake -version
qmake -v

都有输出的话则正常。

还需要添加 qt 的动态链接库:

1
2
3
4
cd /etc/ld.so.conf.d
echo -e "/home/zbttl/Qt/5.15.2/gcc_64/lib\n/home/zbttl/Qt/Tools/QtCreator/lib/qtcreator"|sudo tee qt-5.15.2-x86_64.conf
sudo ldconfig
cd

安装 grpc、protobuf 、opengl 库

1
2
sudo apt-get install libprotobuf-dev protobuf-compiler libgrpc-dev libgrpc++-dev protobuf-compiler-grpc -y
sudo apt install mesa-common-dev -y

编译 qv2ray 发行版

1
2
3
4
5
6
7
8
9
10
git clone https://github.com/Qv2ray/Qv2ray.git --recursive
cd Qv2ray
mkdir build; cd build;
cmake -DCMAKE_INSTALL_PREFIX=/home/zbttl/q2 -DCMAKE_BUILD_TYPE=Release -GNinja ..
#如果没装Ninja,也可以不用Ninja,Ninja编译时是显示编译到多少个包,普通编译则是显示百分比
#cmake -DCMAKE_INSTALL_PREFIX=/home/zbttl/q2 -DCMAKE_BUILD_TYPE=Release ..
cmake --build . --parallel $(nproc)
#装到系统路径中的话,要配合 sudo
#sudo -s
cmake --install .

运行

1
2
cd /home/zbttl/q2/bin/
./qv2ray

有可能会报错哦。需要安装 xcb 库。

1
sudo apt install libxcb-xinerama0 -y

未曾设想的道路(雾)

换用 qt6 编译 dev 版

新界面只有 dev 版支持。编译 dev 版则需要安装 qt6。

需要多装一个库:

1
sudo apt-get install libcurl4-openssl-dev -y

和装 qt5 的步骤接近,除了通过安装程序安装时选择 qt 6.1.2 外,还有编译的参数要做出修改:

1
2
3
4
5
6
git clone -b dev-v3 https://github.com/Qv2ray/Qv2ray.git --recursive
cd Qv2ray
mkdir build; cd build;
#如果是编译dev版可省略-DQV2RAY_QT6=ON参数,编译发行版必须加入(不过也有可能加上都编译不了,所以编译发行版建议用qt5)
cmake -DCMAKE_INSTALL_PREFIX=/home/zbttl/q2 -DCMAKE_BUILD_TYPE=Release -GNinja -DQV2RAY_QT6=ON ..
cmake --build . --parallel $(nproc)

但到这里,我还有一个小目标:

编都编了,干脆能手动编译的都手动编译吧!apt 装的版本虽然能用,但确实有点旧了。

大概有四样东西可以手动编译,分别是 openssl,grpc+protobuf、cmake 和 qt,这几样东西更新快且编译新软件的时候经常对他们有版本号上的需求。严格意义说,opengl 和 ninja 也是能手工编译的,但这两样东西似乎比较稳定,没啥必要手工编译。

手工编译 openssl

可以替换上面用到的 libcurl4-openssl-devlibssl-dev 两个库。(但不要去卸载 openssl,否则使用 wget 等命令时就会报警证书错误。不敲 sudo rm -r /usr/local/ssl/certs&&sudo ln -s /etc/ssl/certs /usr/local/ssl 这个命令也会造成证书报警,但如果卸了 openssl,敲了这个命令也没用。)

1
sudo apt remove libcurl4-openssl-dev libssl-dev -y

下载的版本是 1.1.1f,现在应该还有更新的版本,但别用 3 开头的 alpha 版。

1
2
3
4
5
6
7
8
9
10
wget https://www.openssl.org/source/openssl-1.1.1i.tar.gz --no-check-certificate
tar xzvf openssl-1.1.1i.tar.gz
cd openssl*/
./config
make
make test
sudo make install
sudo ldconfig
sudo rm -r /usr/local/ssl/certs&&sudo ln -s /etc/ssl/certs /usr/local/ssl
cd

安装完确认一下

1
2
3
openssl version

pkg-config --libs openssl

应有类似输出:

1
2
3
OpenSSL 1.1.1f  31 Mar 2020

-L/usr/local/lib -lssl -lcrypto

手动安装 cmake

qt6 必须要 cmake 3.16 以后版本才能编译,好死不死 apt 里带的版本是 3.15。之前用了 qt 官方的在线安装程序,安装的是 3.19,这才勉强能用。新版 grpc 也强制要求使用 cmake 而不是 make 了,本来想和 qt 编译写在一起,现在把这个步骤提前到这里。

cmake 官方直接给了最新的安装脚本,我们直接用就好了,懒得编译了。

可以到 https://github.com/Kitware/CMake/releases 获取最新 release 版本号:

1
2
3
#参考https://sobaigu.com/shell-get-lastest-version-from-github.html命令行直接获取版本号
cmake_new_ver=$(wget -qO- -t1 -T2 "https://api.github.com/repos/Kitware/CMake/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g'|sed 's/[a-z]*//g')
#或者是 cmake_new_ver=$(wget -qO- -t1 -T2 "https://api.github.com/repos/Kitware/CMake/releases/latest" | jq -r '.tag_name'|sed 's/[a-z]*//g')

下载运行

1
2
3
4
sudo apt remove cmake -y
wget https://github.com/Kitware/CMake/releases/download/v${cmake_new_ver}/cmake-${cmake_new_ver}-linux-x86_64.sh
chmod +x cmake-${cmake_new_ver}-linux-x86_64.sh
sudo bash cmake-${cmake_new_ver}-linux-x86_64.sh --prefix=/usr/ --exclude-subdir

如果在使用 ubuntu 且不在意空间的话,也可以通过 snap 安装:

1
sudo snap install cmake

手工编译 grpc 和 protobuf

参考文章

官方文档:

其他使用者的使用心得:

代替了 libprotobuf-devprotobuf-compilerlibgrpc-devlibgrpc++-dev 四个包。

1
2
sudo apt remove libprotobuf-dev protobuf-compiler libgrpc-dev libgrpc++-dev -y
sudo apt install zlib1g-dev -y

protobuf 包含在 grpc 中,所以一起拿下来就好了。cmake 这里,如果全部都手动编译的话,先参考下面手工安装 cmake 章节(因为 grpc 并不要求 cmake 版本,所以用 apt 版本的 cmake 也可以)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
git clone --recurse-submodules https://github.com/grpc/grpc

cd grpc/third_party/protobuf/
git submodule update --init --recursive
./autogen.sh
./configure --prefix=/usr
make -j $(nproc)
sudo make install
sudo ldconfig
cd ../..
cd third_party/abseil-cpp
cmake -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_POSITION_INDEPENDENT_CODE=TRUE
make -j $(nproc)
sudo make install
cd ../..
cmake -DgRPC_INSTALL=ON \
-DgRPC_BUILD_TESTS=OFF \
-DCMAKE_INSTALL_PREFIX=/usr \
-DgRPC_SSL_PROVIDER=package
make -j $(nproc)
sudo make install
cd ..

安装完确认一下

1
protoc --version

应有输出

1
libprotoc 3.15.8

另外还可以通过运行 example 测试,可参照参考文章 4。

编译安装 qt6

说实话,实操后感觉并不推荐大家去单独编译 qt,坑又多,空间占用又大(大概要 17g 左右),编译时间又久,纯属自己好奇,活该(

先装依赖。由于没有 cmake clean 这种类似的命令,因为编译时间比较久,万一哪个依赖没装导致编译中出错,就得删掉整个文件夹重新编译,得不偿失,所以依赖尽量装全一点;除了上面文章提到的依赖(openssl、cmake,grpc 和 protobuf 可以不用),还装了以下这些:

1
sudo apt install  libgl1-mesa-dev libxcb*-dev libfontconfig1-dev libxkbcommon-x11-dev python libgtk-3-dev mesa-common-dev libglu1-mesa-dev -y

qt 官网寻找对应版本的源码包,这里我下了 6.1.0 single 目录下的 zip 版本:

1
2
3
4
wget https://download.qt.io/archive/qt/6.1/6.1.0/single/qt-everywhere-src-6.1.0.zip
sudo apt install unzip -y
unzip qt-everywhere-src-6.1.0.zip
cd qt-everywhere-src-6.1.0

但这时如果直接 configure 会报错,因为下下来的 zip 文件里换行符似乎是 windows 格式的 CFLR。。。

应该下 tar.xz 包的,算了,下都下了,搞个工具转换一下吧:

1
2
sudo apt install dos2unix -y
find . -type f -print0 | xargs -0 dos2unix

编译安装:

1
2
3
4
5
6
7
8
9
10
11
12
./configure -openssl --prefix=/usr/local/qt6
export PATH=/usr/local/qt6/bin:$PATH
cmake --build . --parallel $(nproc)
sudo cmake --install .

echo "export PATH=/usr/local/qt6/bin:\$PATH" >> ~/.bashrc
source ~/.bashrc

cd /etc/ld.so.conf.d
echo -e "/usr/local/qt6/lib"|sudo tee qt-6-x86_64.conf
sudo ldconfig
cd

此时输入

1
qmake -v

应有输出

1
2
QMake version 3.1
Using Qt version 6.1.0 in /usr/local/qt6/lib

安全绳

如果你看完了上面的内容才来到这里,有两种可能:

  1. 你很幸运,一次过了;
  2. 你在云,根本没动手!

保守估计有起码一半的人会在半路跳进坑里,那你就得在这里找绳子啦~

关于 grpc 和 protobuf 的版本问题

估计有一部分人(包括我)之前接触过 go 版本的 grpc 和 protobuf,刚开始就会心生疑问:这俩玩意用 go 或者 pip 装不是更快吗?

很遗憾。。。并不行,必须用 c 版本的,还得编译出 lib 版本的库装到系统中才能正常使用。

ssl 错误

常见于 qv2ray 和 qt6 编译中出现。

qv2ray 编译中出现,一般是因为

  1. 版本不匹配,比如自己编译了 openssl,又安装了 libssl-dev
  2. 编译 grpc 的时候没有加参数 -DgRPC_SSL_PROVIDER=package,参见 编译到最后一步报错 · Discussion #1509 · Qv2ray/Qv2ray

在 qt 编译中出现,大概率是 libssl-dev 没装或者是 configure 后补装的。

另参考:

库错误

最大的坑。严格意义上上面的 ssl 错误也可以归类为库错误的范畴。

原因有:

  1. 新编译软件的库没有放到 ldconfig 中;
  2. 没装库
  3. 库装晚了(常见于 qt 编译)
  4. 既用了 apt 装库也编译安装了一份,apt 装的优先被读取但编译其他东西的时候用的是编译安装的库因此运行时报错。

有那么几个检查方法:

  1. 编译时报错。可用 pkg-config 命令,类似

    1
    2
    pkg-config --libs openssl
    pkg-config --libs xcb

    有输出证明库正常安装,没有就是缺了。

  2. 运行时报错。比如常见的运行 qv2ray 二进制时报 xcb 相关的错误,可使用 ldd 命令:

    1
    ldd ./qv2ray

    如果有库没找到 / 没装,会有提示。

解决方法就是使用 sudo find / -name 命令搜查相关文件确定原因,然后根据原因对症下药:

  1. 加入相关路径到 ldconfig(可参考上面 qt 安装和编译的文章)
  2. apt 装库或者编译库
  3. 装库后删除已编译部分重新编译,或者删除整个项目并重新拉取。
  4. 配置 LD_LIBRARY_PATH 变量。参考 qt 错误第一条。

多线程编译错误

在使用类似

1
make -j $(nproc)

这类命令时,有可能出现各种编译错误问题,例如这个报错:

1
lib error: relink 'libprotoc.la' with the above command before installing it

解决方法有:

  1. 如果可能,使用 make clean 命令后重新 make;没有 make clean 命令的话就只能重新拉取项目代码;
  2. 使用单线程或手动指定进程数而不是使用 $(nproc) 变量。注意,不要指定过大的线程数或者不指定线程数(比如直接使用 make -j 命令),错误的进程数可能消耗过大的内存导致系统卡死。

qt 错误

qt 有可能遭遇编译错误;也有可能编译安装成功后发现 qv2ray 运行不了,因为编译的 qt 组件不全。

自编译 qt 后不慎在 apt 内安装了对应包导致报错

报错代码类似于 version 'Qt_5.15' not found (required by xxxx)

解决方法是配置 LD_LIBRARY_PATH。让自编译的 qt 库优先被使用。

1
2
3
4
echo "export LD_LIBRARY_PATH=/home/zbttl/Qt/5.15.2/gcc_64/lib"|sudo tee /etc/bash.bashrc

echo "export LD_LIBRARY_PATH=\$HOME/Qt/5.15.2/gcc_64/lib" >> ~/.bashrc
source ~/.bashrc

防止 configure 时库不全

运行 ./configure 命令后,会在 qt 目录下生成一个 CMakeCache.txt 文件,里面就是本次 qt 编译所支持的功能。如果此时环境内依赖不全,就会导致组件缺失。这里放出我自己测试能够成功运行 qv2ray 的 qt6 编译组件清单文件,出问题的话可以用 vscode 对一下。

另外使用 ./configure 命令时,可以考虑带上 -openssl-opengl 等参数(上面的教程例子中已经带上了 -openssl),如果找不到相应的库,就会直接报错告诉你差这些而不是各种一闪而过告诉你准备完成。

已编译安装的 qt6 测试

可以写一个小测试项目,因为目前我遇到的安装后的 qt 方面的三个问题,分别对应 openssl、opengl、xcb 三个问题,而我编的这个小程序要是能够正常编译运行,就证明不存在那三个问题。

  1. 新建一个文件夹,文件夹里面放上名为 test.cpp 的文件,写入以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <QApplication>
    #include <QLabel>
    #include <QDebug>
    #include <QSslSocket>

    int main(int argc,char *argv[])
    {
    QApplication app(argc,argv);
    //qDebug()<<QSslSocket::sslLibraryBuildVersionString();
    QLabel *Label = new QLabel(QSslSocket::sslLibraryBuildVersionString());
    Label->show();
    return app.exec();
    }
  2. 生成 pro 文件

    1
    qmake -project
  3. 生成 stash 文件

    1
    qmake
  4. 修改 pro 文件,加入

    1
    2
    QT       += widgets \
    network
  5. 编译

    1
    make
  6. 运行

    1
    ./test

    如果没问题就会弹出带有当前系统 openssl 版本号的弹窗。

    后续如果想要显示其他内容,修改 test.cpp 内容并 make 就可以。不要执行 qmake,否则 pro 文件里这行就要重新写了。

运行后报错,查看具体信息

输入

1
export QT_DEBUG_PLUGINS=1

此时再运行 qt 程序就会弹出更详细的错误信息。

qv2ray 官方部署参考文章和其他参考文章


二号坑

总算是搞定了。。。可是等一下,新界面呢?

还有怎么没有中文,按下插件按钮时报错?(似乎只有 qt5 编译的发行版没有发生这种错误)

另外编译出来的东西怎么打包呢。。。

没有新界面是编译过程有问题吗?但后来我在 tg 群中发现,其实如果只是 ubuntu 下想要装新版本,qv2ray 官方搞了这个东西:Qv2ray Debian Repository (Nightly) | debian-dev,装这个就好了。。。。但这里面的新版本界面我也没调出来。

嘛,看来,还得折腾。

(21.8.9 更新) 在项目的 github action 里面看到了各种不同版本的 qv2ray(所以是实际上,要下载最新版本的 qv2ray,应该去 action 里面找而不是到 release 里面)。

里面这些中,windows 专用的就 Qv2ray Windows InstallerQv2ray build matrix - cmake Qv2ray build matrix - cmake,只有最后一个是用 qt6 编译的新版;点开,果然有 qml 和 qwidget 两种,分布对应着新界面和旧界面;版本间还有 RelWithDebInforelease 两种编译模式上的区别,把前者当做性能更好的 debug 版本就行了。

不过嘛。。。。一个是,这几个包解压后,打开里面的 qv2ray 可执行文件会报错 openssl 不存在,需要先用 chocolatey 装一个;其次就是,qml 版本双击后一点反应也没有。最后翻了一下群记录,发现

意思是。。。白干了?

(21.8.17 更新)

业 界 冥 灯