Files
dotfiles/memo/lfs.md
2025-12-30 23:10:20 +01:00

388 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
记录一些在安装 LFS 时踩的一些坑和其他值得一提的事情。
> [!IMPORTANT]
>
> 本文中的 LFS 仅指代 12.4 SysV 版本
## LFS
以下内容按照对应章节划分:
- [2.4.1.4](https://www.linuxfromscratch.org/lfs/view/stable/chapter02/creatingpartition.html)
虽然此处列举了很多分区,例如 `/opt``/usr/sources` 等,但我并不喜欢,也并不觉得有必要将根目录分得如此细碎。对于 UEFI 引导的系统来说btw 我不认为 BIOS 引导和 UEFI 引导的配置难度的差距有很多观点说得那样巨大)除去可选的 Swap 分区外通常 `/boot/efi``/boot``/``/home` 几个分区就足够了。
- [2.6](https://www.linuxfromscratch.org/lfs/view/stable/chapter02/aboutlfs.html)
`$LFS` 变量很重要!建议写到 Host 的 `/root/.bash_profile``/etc/profile.d/...` 或类似作用的配置文件里防止忘记设置。
- [2.6](https://www.linuxfromscratch.org/lfs/view/stable/chapter02/mounting.html)
可以用一个小脚本挂载分区,推荐使用文件系统 UUID 而非诸如 `/dev/sdc1` 这样的绝对路径:
```bash
#!/bin/bash
[ -z "$LFS" ] && exit 1
set -euo pipefail
UUID_EFI="{ESP_UUID}"
UUID_BOOT="{BOOT_UUID}"
UUID_LFS="{ROOT_UUID}"
UUID_HOME="{HOME_UUID}"
ensure_device() {
local uuid="$1"
local path="/dev/disk/by-uuid/$uuid"
if [ ! -e "$path" ]; then
echo "Device with UUID $uuid not found at $path" >&2
exit 1
fi
echo "$path"
}
mkdir_p() {
local d="$1"
if [ ! -d "$d" ]; then
mkdir -p "$d"
fi
}
mkdir_p "$LFS"
DEV_LFS="$(ensure_device "$UUID_LFS")"
DEV_BOOT="$(ensure_device "$UUID_BOOT")"
DEV_EFI="$(ensure_device "$UUID_EFI")"
DEV_HOME="$(ensure_device "$UUID_HOME")"
mount "$DEV_LFS" "$LFS"
mkdir_p "$LFS/boot"
mount "$DEV_BOOT" "$LFS/boot"
mkdir_p "$LFS/boot/efi"
mount "$DEV_EFI" "$LFS/boot/efi"
mkdir_p "$LFS/home"
mount "$DEV_HOME" "$LFS/home"
```
- [3](https://www.linuxfromscratch.org/lfs/view/stable/chapter03/chapter03.html)
强烈建议通过镜像站下载打包好的所有源代码和 patch否则某些源服务器的下载速度即便在非受限网络环境也很感人。
- [4.5](https://www.linuxfromscratch.org/lfs/view/stable/chapter04/aboutsbus.html)
SBU 只提供大概的预期耗时范围,误差是很大的,尤其对于 BLFS 书中的一些编译时间很长的包(如 QtFirefox来说。
- [5](https://www.linuxfromscratch.org/lfs/view/stable/chapter05/chapter05.html)
从这一章开始将会手动编译大量的包,其中有不少操作是重复的,例如 `tar -xf``cd``rm -rf` 等,为此可以写一个小脚本节省时间:
```bash
#!/bin/bash
set -euo pipefail
[ -z "${1:-}" ] && {
echo "Usage: $0 <tarball>"
exit 1
}
tarball=$(realpath "$1")
dir=$(tar -tf "$tarball" | sed -e 's/^\.\///' | cut -d/ -f1 | sort -u)
if [ "$(echo "$dir" | wc -l)" -ne 1 ]; then
echo "Error: Tarball must contain a single top-level directory."
exit 1
fi
trap 'cd .. && rm -rf "$dir" && echo "Removed $dir"' EXIT
tar -xvf "$tarball"
if [ ! -d "$dir" ]; then
echo "Error: Failed to extract directory: $dir"
exit 1
fi
cd "$dir"
echo "Spawning shell. Type 'exit' to finish and cleanup."
bash
```
它的作用是解压一个只含有一个顶层目录的 tarballcd 进入解压后得到的目录,生成一个 shell并在这个 shell 退出时清理先前解压得到的文件。
- [5](https://www.linuxfromscratch.org/lfs/view/stable/chapter05/chapter05.html)
另外还有对于在 LFS 与 BLFS 中编译包的一些通用建议:
1. 通常来说,应该(或者说请务必)在编译和安装一个包后完全删除它的目录,仅有少数例外:
- linux保留构建树可以缩短重新构建耗时或至少保留 config 便于复原配置)
- blfs-bootscripts在 BLFS 中)
注意一些需要多次编译的包,例如 gcc也应该在每次编译与安装后完全删除目录。
2. 通常来说,建议将所有相关的 tarball 和 patch 下载在同一个目录中,并且将 tarball 也解压在这个目录中。如果目录层级与该默认情况不一致的话必须修改 LFS 书中提供的命令中对应的相对路径。
3. 如果和我一样使用 UEFI 引导,那么大概率将会在 [8.64. GRUB-2.12](https://www.linuxfromscratch.org/lfs/view/stable/chapter08/grub.html) 第一次接触 BLFS。和 LFS 不同BLFS 中大多数包都是可选的,一个包可能会依赖其他包,这些依赖分为三个层级:
- Required
- Recommended
- Optional
其中 `Required` 是必要的依赖;`Recommended` 通常建议当成 `Required` 看待,除非明确知道对应包的用途和不安装它的影响;`Optional` 酌情安装,很多时候是文档相关的依赖。
更多关于安装 UEFI 版 GRUB 的话题将会在之后的章节中讨论。
4. 如果想要的包在 LFS 和 BLFS 包中都没有,例如 fish、libglvnd、flatpak 等,不妨先检查 [GLFS](https://www.linuxfromscratch.org/glfs/) 和 [SLFS](https://www.linuxfromscratch.org/slfs/) 或同系列的其他书中是否有涉及,如果有的话将节约很多学习和试错成本。
- [7.4. Entering the Chroot Environment](https://www.linuxfromscratch.org/lfs/view/stable/chapter07/chroot.html)
对于 chroot 也可以使用一个小脚本显著地改善体验:
```bash
#!/bin/bash
[ -z "$LFS" ] && exit 1
# mount virtual file systems
mkdir -pv $LFS/{dev,proc,sys,run}
mount -v --bind /dev $LFS/dev
mount -vt devpts devpts -o gid=5,mode=0620 $LFS/dev/pts
mount -vt proc proc $LFS/proc
mount -vt sysfs sysfs $LFS/sys
mount -vt tmpfs tmpfs $LFS/run
if [ -h $LFS/dev/shm ]; then
install -v -d -m 1777 $LFS$(realpath /dev/shm)
else
mount -vt tmpfs -o nosuid,nodev tmpfs $LFS/dev/shm
fi
# cleanup (defer)
cleanup() {
echo "Cleaning up..."
mountpoint -q $LFS/dev/shm && umount $LFS/dev/shm
umount $LFS/dev/pts
umount $LFS/{sys,proc,run,dev}
}
trap cleanup EXIT INT TERM
# chroot
chroot "$LFS" /usr/bin/env -i \
HOME=/root \
TERM="$TERM" \
PS1='(lfs chroot) \u:\w\$ ' \
PATH=/usr/local/bin:/usr/bin:/usr/sbin \
MAKEFLAGS="-j$(nproc)" \
TESTSUITEFLAGS="-j$(nproc)" \
/bin/bash --login
```
它的作用是自动挂载一系列虚拟文件系统,同时在退出 chroot 时自动清理。
> [!NOTE]
>
> 此处并不建议使用 `arch-install-scripts` 提供的 `arch-chroot` 偷懒,除非明确知道自己在做什么。
- [8.64. GRUB-2.12](https://www.linuxfromscratch.org/lfs/view/stable/chapter08/grub.html)
对于 UEFI 引导的系统,此时需要跳转 BLFS 安装 GRUB。为避免过早地陷入依赖地狱建议仅按照顺序安装以下包
1. [efivar-39](https://www.linuxfromscratch.org/blfs/view/12.4/postlfs/efivar.html)
2. [Popt-1.19](https://www.linuxfromscratch.org/blfs/view/12.4/general/popt.html)
3. [efibootmgr-18](https://www.linuxfromscratch.org/blfs/view/12.4/postlfs/efibootmgr.html)
4. [GRUB-2.12 for EFI](https://www.linuxfromscratch.org/blfs/view/12.4/postlfs/grub-efi.html)
这对于引导系统来说已经足够用了。如果需要的话可以再之后再补上文档等其他附加选项。
- [10.2. Creating the /etc/fstab File](https://www.linuxfromscratch.org/lfs/view/stable/chapter10/fstab.html)
可以使用 `arch-install-scripts` 提供的 `genfstab` 偷懒。但必须注意的是不同于常见的发行版LFS 需要在 `/etc/fstab` 中指定一系列虚拟文件系统,如果不这样做的话会导致无法启动。
- [10.3. Linux-6.16.1](https://www.linuxfromscratch.org/lfs/view/stable/chapter10/kernel.html)
内核配置是整本 LFS 书中最具有挑战的环节。对此我可以总结出几点建议:
- 复刻并裁剪现有配置
如果将要使用正在构建的 LFS 系统的机器和 Host 完全相同,可以将 Host 现在运行的内核的配置文件搬过来,同时仅启用当前 Host 加载的内核模块,这将极大地减小配置难度和缩短构建耗时:
在 Host 上运行:
```bash
# 进入内核源码目录
cd /path/to/lfs/sources/linux-6.16.1
# 导出当前内核配置(如果 Host 有 /proc/config.gz
zcat /proc/config.gz > .config
```
在 chroot 环境中运行:
```bash
# 进入内核源码目录
cd /path/to/lfs/sources/linux-6.16.1
# 裁剪配置
make localmodconfig
```
如果内核版本不一致,会在 `make localmodconfig` 时出现很多交互选项,建议全部保持默认,或使用 `make olddefconfig` 来自动处理。
在此之后,仍建议按照 LFS 书中的建议检查和调整配置选项。
> [!NOTE]
>
> 即使不打算直接使用 Host 的内核配置Host 加载的内核模块也能为 LFS 内核的配置提供很好的参考,例如当一个门类下有很多并列且可选的选项时,可以优先启用 Host 当前加载的模块对应的选项。
- BLFS 要求的内核配置
除 LFS 书中列出的内核配置选项外BLFS 中单独列出了所有涉及的包需要的内核配置选项,建议在配置内核时参考 [BLFS Index of Kernel Settings](https://www.linuxfromscratch.org/blfs/view/12.4/longindex.html#kernel-config-index) 一节,确保启用了所有必要的选项,否则后续可能需要多次重新编译内核。
- 对于 NVIDIA
如果之后会安装闭源的 NVIDIA 驱动程序,则不建议在内核中启用 nouveau 驱动,因为这会导致闭源驱动无法正常工作,后续禁用 nouveau 将会是额外的工作量。
- \* or M
对于大多数选项建议选择模块化M而非内置\*)。这将显著减少内核体积,并且提高灵活性。但有几种情况例外:
- 引导相关的选项(例如 EFI 支持)必须内置,否则无法引导。
- 一些必要的文件系统(例如 ext4建议内置否则可能无法挂载根文件系统。
也有必须模块化的情况,例如:
- 需要固件支持的驱动程序(如 i915除非使用 initrd 或将固件内置到内核中。
## BLFS
绝大多数的建议都已在 LFS 部分体积,这里只做少数补充:
- 阅读顺序
BLFS 并不像 LFS 那样有线性的章节顺序,但仍建议先顺序阅读直到 [After LFS Configuration Issues](https://www.linuxfromscratch.org/blfs/view/stable/postlfs/config.html) 章节**结束**再按自己的需要安装各种包。
- [Mesa-25.1.8](https://www.linuxfromscratch.org/blfs/view/stable/x/mesa.html)
此处记录使用 Intel iGPUi915和 NVIDIA dGPUNVIDIA 专有驱动)的混合显卡系统时的配置方法:
```
meson setup build \
--prefix=$XORG_PREFIX \
--buildtype=release \
-D platforms=x11,wayland \
-D gallium-drivers=iris,llvmpipe \
-D vulkan-drivers=intel,swrast \
-D valgrind=disabled \
-D video-codecs=all \
-D libunwind=disabled \
-D glvnd=enabled
```
其中:
- `-D gallium-drivers=iris,llvmpipe`
- 仅启用 Intel iGPU 的驱动,因为 NVIDIA 专有驱动自带完整的 OpenGL 支持,不需要 Mesa 提供。
- 同时启用 llvmpipe 用于软件渲染以防万一。
- 对于较新Gen 8 及更新)的硬件,`crocus`(适用于 Gen 4 到 Gen 7和 `i915`(更老)用户态 OpenGL 驱动已被废弃,不应再使用。注意此处的 i915 和内核中的 i915 内核驱动是不同的东西。
- `-D vulkan-drivers=intel,swrast`:启用 Intel iGPU 的 Vulkan 支持,同时启用 swrast 以防万一。
- `-D glvnd=enabled`:启用 GLVND 支持以便和 NVIDIA 专有驱动兼容。libglvnd 需要[在 GLFS 书中安装](https://glfs-book.github.io/glfs/shareddeps/libglvnd.html)。
- [Qt-6.9.2](https://www.linuxfromscratch.org/blfs/view/stable/x/qt6.html)
此版本的 Qt 在构建时可能会出现错误,日志的一部分如下:
<details>
<summary>错误日志</summary>
```log
[10697/11095] Linking CXX shared mo...s/position/libqtposition_geoclue2.so
FAILED: [code=1] qtbase/plugins/position/libqtposition_geoclue2.so
: && /usr/bin/c++ -fPIC -DNDEBUG -O2 -shared -Wl,--no-undefined -Wl,--version-script,/home/kolkas/qtbuild/qt-everywhere-src-6.9.2/qtpositioning/src/plugins/position/geoclue2/QGeoPositionInfoSourceFactoryGeoclue2Plugin.version -Wl,-z,relro,-z,now -Wl,--enable-new-dtags -o qtbase/plugins/position/libqtposition_geoclue2.so qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/client_interface.cpp.o qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/location_interface.cpp.o qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/manager_interface.cpp.o qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/geocluetypes.cpp.o qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosourcefactory_geoclue2.cpp.o -Wl,-rpath,/home/kolkas/qtbuild/qt-everywhere-src-6.9.2/qtbase/lib: qtbase/lib/libQt6DBus.so.6.9.2 qtbase/lib/libQt6Positioning.so.6.9.2 qtbase/lib/libQt6Core.so.6.9.2 && :
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o:(.data.rel.ro+0x160): multiple definition of `OrgFreedesktopGeoClue2ClientInterface::staticMetaObject'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:(.data.rel.ro+0x120): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o:(.data.rel.ro+0xc0): multiple definition of `OrgFreedesktopGeoClue2LocationInterface::staticMetaObject'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:(.data.rel.ro+0x80): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o:(.data.rel.ro+0x40): multiple definition of `OrgFreedesktopGeoClue2ManagerInterface::staticMetaObject'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:(.data.rel.ro+0x0): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2ClientInterface::metaObject() const':
qgeopositioninfosource_geoclue2.cpp:(.text+0x30): multiple definition of `OrgFreedesktopGeoClue2ClientInterface::metaObject() const'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x0): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2LocationInterface::metaObject() const':
qgeopositioninfosource_geoclue2.cpp:(.text+0x50): multiple definition of `OrgFreedesktopGeoClue2LocationInterface::metaObject() const'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x20): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2ManagerInterface::metaObject() const':
qgeopositioninfosource_geoclue2.cpp:(.text+0x70): multiple definition of `OrgFreedesktopGeoClue2ManagerInterface::metaObject() const'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x40): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2ClientInterface::LocationUpdated(QDBusObjectPath const&, QDBusObjectPath const&)':
qgeopositioninfosource_geoclue2.cpp:(.text+0xb0): multiple definition of `OrgFreedesktopGeoClue2ClientInterface::LocationUpdated(QDBusObjectPath const&, QDBusObjectPath const&)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x60): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2ClientInterface::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)':
qgeopositioninfosource_geoclue2.cpp:(.text+0x2af0): multiple definition of `OrgFreedesktopGeoClue2ClientInterface::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0xc0): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2LocationInterface::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)':
qgeopositioninfosource_geoclue2.cpp:(.text+0x3440): multiple definition of `OrgFreedesktopGeoClue2LocationInterface::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0xb60): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2ManagerInterface::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)':
qgeopositioninfosource_geoclue2.cpp:(.text+0x4140): multiple definition of `OrgFreedesktopGeoClue2ManagerInterface::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x1000): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2ClientInterface::qt_metacast(char const*)':
qgeopositioninfosource_geoclue2.cpp:(.text+0x4bc0): multiple definition of `OrgFreedesktopGeoClue2ClientInterface::qt_metacast(char const*)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x1a80): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2LocationInterface::qt_metacast(char const*)':
qgeopositioninfosource_geoclue2.cpp:(.text+0x4c20): multiple definition of `OrgFreedesktopGeoClue2LocationInterface::qt_metacast(char const*)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x1ae0): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2ManagerInterface::qt_metacast(char const*)':
qgeopositioninfosource_geoclue2.cpp:(.text+0x4c80): multiple definition of `OrgFreedesktopGeoClue2ManagerInterface::qt_metacast(char const*)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x1b40): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2ClientInterface::qt_metacall(QMetaObject::Call, int, void**)':
qgeopositioninfosource_geoclue2.cpp:(.text+0x4ce0): multiple definition of `OrgFreedesktopGeoClue2ClientInterface::qt_metacall(QMetaObject::Call, int, void**)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x1ba0): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2LocationInterface::qt_metacall(QMetaObject::Call, int, void**)':
qgeopositioninfosource_geoclue2.cpp:(.text+0x4dc0): multiple definition of `OrgFreedesktopGeoClue2LocationInterface::qt_metacall(QMetaObject::Call, int, void**)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x1c80): first defined here
/usr/bin/ld: qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/qgeopositioninfosource_geoclue2.cpp.o: in function `OrgFreedesktopGeoClue2ManagerInterface::qt_metacall(QMetaObject::Call, int, void**)':
qgeopositioninfosource_geoclue2.cpp:(.text+0x4e10): multiple definition of `OrgFreedesktopGeoClue2ManagerInterface::qt_metacall(QMetaObject::Call, int, void**)'; qtpositioning/src/plugins/position/geoclue2/CMakeFiles/QGeoPositionInfoSourceFactoryGeoclue2Plugin.dir/QGeoPositionInfoSourceFactoryGeoclue2Plugin_autogen/mocs_compilation.cpp.o:mocs_compilation.cpp:(.text+0x1cd0): first defined here
collect2: error: ld returned 1 exit status
[10716/11095] Building CXX object q...hell.dir/qwaylandqtshellchrome.cpp.o
ninja: build stopped: subcommand failed.
```
</details>
原因是源文件包含了多余的 moc 文件导致重复定义。出问题的源文件有两个,可以通过以下命令修复:
```bash
sed -i -E '/#include "moc_.+/d' qtpositioning/src/plugins/position/geoclue2/qgeopositioninfosourcefactory_geoclue2.cpp qtpositioning/src/plugins/position/geoclue2/qgeopositioninfosource_geoclue2.cpp
```