Linux 上 Qt6 的 Bazel 规则:如何复制所需的库?
Bazel rules for Qt6 on Linux: How to copy required libs?
目前,我正在进行一项支持 Qt6 via Bazel. My code can be found here 的实验。
在安装了 Bazel 和 GCC9 的 Linux 上,您可以通过以下方式测试我的 Qt6 Bazel 规则:
git clone https://github.com/Vertexwahn/rules_qt6
cd rules_qt6
bazel run --config=gcc9 //:Qt6HelloWorld # run Qt6HelloWorld binary
尝试 运行 Qt6HelloWorld
时会报告此错误:
/home/user/.cache/bazel/_bazel_$USER/196a14423fc09522ef7bd657344d1cd0/execroot/Qt6Testbed/bazel- out/k8-fastbuild/bin/Qt6HelloWorld:
error while loading shared libraries: libQt6Network.so.6: cannot open shared object file: No such file or directory
Qt6.1.0中好像有libQt6Network.so
、libQt6Network.so.6
、libQt6Network.so.6.1.0
。如果我将这些文件复制到 bazel-out/k8-fastbuild/bin
(Qt6HelloWorld
二进制文件所在的位置),我仍然会收到此错误。
关于如何修复此错误的任何想法?
关于如何扩展我的规则以复制那些所需的库以使 Bazel 满意的任何想法?
这是一个迟到的回复,但我刚刚看到你的规则试图做一些非常相似的事情。
这里的主要问题似乎是在您的规则中引用了 libQt*.so
,它们是指向实际库的符号链接。 Bazel 正确地创建了一个指向 runfiles 目录(在 _solib_k8
下)的符号链接,并将从主二进制文件到 _solib_k8
下的路径的相对路径放入二进制文件的 RUNPATH
中。但是,二进制文件中的 DT_NEEDED
条目引用以 .so.6
结尾的文件(可能是因为那是库的 SONAME
)。在任何目录中都找不到这些,给你上面的错误。以下是一些详细信息:
$ readelf -d [...]/execroot/Qt6Testbed/bazel-out/k8-fastbuild/bin/Qt6HelloWorld
0x0000000000000001 (NEEDED) Shared library: [libQt6Network.so.6]
0x0000000000000001 (NEEDED) Shared library: [libQt6Qml.so.6]
0x0000000000000001 (NEEDED) Shared library: [libQt6Core.so.6]
0x0000000000000001 (NEEDED) Shared library: [libQt6Gui.so.6]
0x0000000000000001 (NEEDED) Shared library: [libQt6Widgets.so.6]
[...]
0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Unetwork_Ulinux_Uimport___Ulib:$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Uqml_Ulinux_Uimport___Ulib:$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Ucore_Ulinux_Uimport___Ulib:$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Ugui_Ulinux_Uimport___Ulib:$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Uwidgets_Ulinux_Uimport___Uli]
[...]
检查 RUNPATH
上第一个目录中的第一个 .so
,我们得到:
readelf -d [...]/execroot/Qt6Testbed/bazel-out/k8-fastbuild/bin/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Unetwork_Ulinux_Uimport___Ulib/libQt6Network.so
Dynamic section at offset 0x206960 contains 36 entries:
Tag Type Name/Value
[...]
0x000000000000000e (SONAME) Library soname: [libQt6Network.so.6]
[...]
明显的解决方法是对 cc_import
目标使用 libQt*.so.6
,但是我们必须跳过 interface_library
参数,因为显然那个参数必须以 [=22= 结尾].但是,我不确定 cc_import
比 cc_library
有什么好处,至少在 Linux 上是这样。所以我建议使用 cc_library
和简单的 glob
类似 libQt6Core.so*
.
一旦修复,我们将面临下一个问题:libicui18n.so.56
至少需要一个 libQt*.so
库,但我们尚未提供。事实证明它们都需要它,所以我们可以简单地扩展我们的 glob。结果可能如下所示:
[
cc_library(
name = "qt_%s_linux_import" % name,
hdrs = [],
srcs = glob([
"lib/lib%s.so*" % library_name,
"lib/libicu*.so*",
]),
target_compatible_with = ["@platforms//os:linux"],
)
for name, include_folder, library_name, _ in QT_LIBRARIES
]
最后剩下的问题是找不到Qt平台插件。这可以通过 data
提供整个 plugins
目录并设置 QT_QPA_PLATFORM_PLUGIN_PATH
来解决。我们可以将插件放入 filegroup
in qt_6.1.0_linux_desktop_gcc_64.BUILD
filegroup(
name = "plugin_files",
srcs = glob(["plugins/**/*"]),
visibility = ["//visibility:public"],
)
然后在 cc_binary
中将其设为 data
:
cc_binary(
name = "Qt6HelloWorld",
srcs = ["main.cpp"],
deps = [
":qt_core",
":qt_qml",
":qt_widgets",
],
env = select({
"@platforms//os:linux": {
"QT_QPA_PLATFORM_PLUGIN_PATH": "external/qt_6.1.0_linux_desktop_gcc_64/plugins",
},
"@platforms//os:windows": {
# TODO
},
}),
data = select({
"@platforms//os:linux": ["@qt_6.1.0_linux_desktop_gcc_64//:plugin_files"],
"@platforms//os:windows": [],
}),
)
既然我现在已经把所有这些都放在本地了,我将为你的 GitHub 回购创建一个 PR。
目前,我正在进行一项支持 Qt6 via Bazel. My code can be found here 的实验。
在安装了 Bazel 和 GCC9 的 Linux 上,您可以通过以下方式测试我的 Qt6 Bazel 规则:
git clone https://github.com/Vertexwahn/rules_qt6
cd rules_qt6
bazel run --config=gcc9 //:Qt6HelloWorld # run Qt6HelloWorld binary
尝试 运行 Qt6HelloWorld
时会报告此错误:
/home/user/.cache/bazel/_bazel_$USER/196a14423fc09522ef7bd657344d1cd0/execroot/Qt6Testbed/bazel- out/k8-fastbuild/bin/Qt6HelloWorld:
error while loading shared libraries: libQt6Network.so.6: cannot open shared object file: No such file or directory
Qt6.1.0中好像有libQt6Network.so
、libQt6Network.so.6
、libQt6Network.so.6.1.0
。如果我将这些文件复制到 bazel-out/k8-fastbuild/bin
(Qt6HelloWorld
二进制文件所在的位置),我仍然会收到此错误。
关于如何修复此错误的任何想法? 关于如何扩展我的规则以复制那些所需的库以使 Bazel 满意的任何想法?
这是一个迟到的回复,但我刚刚看到你的规则试图做一些非常相似的事情。
这里的主要问题似乎是在您的规则中引用了 libQt*.so
,它们是指向实际库的符号链接。 Bazel 正确地创建了一个指向 runfiles 目录(在 _solib_k8
下)的符号链接,并将从主二进制文件到 _solib_k8
下的路径的相对路径放入二进制文件的 RUNPATH
中。但是,二进制文件中的 DT_NEEDED
条目引用以 .so.6
结尾的文件(可能是因为那是库的 SONAME
)。在任何目录中都找不到这些,给你上面的错误。以下是一些详细信息:
$ readelf -d [...]/execroot/Qt6Testbed/bazel-out/k8-fastbuild/bin/Qt6HelloWorld
0x0000000000000001 (NEEDED) Shared library: [libQt6Network.so.6]
0x0000000000000001 (NEEDED) Shared library: [libQt6Qml.so.6]
0x0000000000000001 (NEEDED) Shared library: [libQt6Core.so.6]
0x0000000000000001 (NEEDED) Shared library: [libQt6Gui.so.6]
0x0000000000000001 (NEEDED) Shared library: [libQt6Widgets.so.6]
[...]
0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Unetwork_Ulinux_Uimport___Ulib:$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Uqml_Ulinux_Uimport___Ulib:$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Ucore_Ulinux_Uimport___Ulib:$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Ugui_Ulinux_Uimport___Ulib:$ORIGIN/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Uwidgets_Ulinux_Uimport___Uli]
[...]
检查 RUNPATH
上第一个目录中的第一个 .so
,我们得到:
readelf -d [...]/execroot/Qt6Testbed/bazel-out/k8-fastbuild/bin/_solib_k8/_U@qt_U6.1.0_Ulinux_Udesktop_Ugcc_U64_S_S_Cqt_Unetwork_Ulinux_Uimport___Ulib/libQt6Network.so
Dynamic section at offset 0x206960 contains 36 entries:
Tag Type Name/Value
[...]
0x000000000000000e (SONAME) Library soname: [libQt6Network.so.6]
[...]
明显的解决方法是对 cc_import
目标使用 libQt*.so.6
,但是我们必须跳过 interface_library
参数,因为显然那个参数必须以 [=22= 结尾].但是,我不确定 cc_import
比 cc_library
有什么好处,至少在 Linux 上是这样。所以我建议使用 cc_library
和简单的 glob
类似 libQt6Core.so*
.
一旦修复,我们将面临下一个问题:libicui18n.so.56
至少需要一个 libQt*.so
库,但我们尚未提供。事实证明它们都需要它,所以我们可以简单地扩展我们的 glob。结果可能如下所示:
[
cc_library(
name = "qt_%s_linux_import" % name,
hdrs = [],
srcs = glob([
"lib/lib%s.so*" % library_name,
"lib/libicu*.so*",
]),
target_compatible_with = ["@platforms//os:linux"],
)
for name, include_folder, library_name, _ in QT_LIBRARIES
]
最后剩下的问题是找不到Qt平台插件。这可以通过 data
提供整个 plugins
目录并设置 QT_QPA_PLATFORM_PLUGIN_PATH
来解决。我们可以将插件放入 filegroup
in qt_6.1.0_linux_desktop_gcc_64.BUILD
filegroup(
name = "plugin_files",
srcs = glob(["plugins/**/*"]),
visibility = ["//visibility:public"],
)
然后在 cc_binary
中将其设为 data
:
cc_binary(
name = "Qt6HelloWorld",
srcs = ["main.cpp"],
deps = [
":qt_core",
":qt_qml",
":qt_widgets",
],
env = select({
"@platforms//os:linux": {
"QT_QPA_PLATFORM_PLUGIN_PATH": "external/qt_6.1.0_linux_desktop_gcc_64/plugins",
},
"@platforms//os:windows": {
# TODO
},
}),
data = select({
"@platforms//os:linux": ["@qt_6.1.0_linux_desktop_gcc_64//:plugin_files"],
"@platforms//os:windows": [],
}),
)
既然我现在已经把所有这些都放在本地了,我将为你的 GitHub 回购创建一个 PR。