diff --git a/memo/lfs.md b/memo/lfs.md new file mode 100644 index 0000000..50d24db --- /dev/null +++ b/memo/lfs.md @@ -0,0 +1,387 @@ +记录一些在安装 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 书中的一些编译时间很长的包(如 Qt,Firefox)来说。 + +- [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 " + 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 + ``` + + 它的作用是解压一个只含有一个顶层目录的 tarball,cd 进入解压后得到的目录,生成一个 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 iGPU(i915)和 NVIDIA dGPU(NVIDIA 专有驱动)的混合显卡系统时的配置方法: + + ``` + 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 在构建时可能会出现错误,日志的一部分如下: + +
+ 错误日志 + + ```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. + ``` + +
+ + 原因是源文件包含了多余的 moc 文件导致重复定义。出问题的源文件有两个,可以通过以下命令修复: + + ```bash + sed -i -E '/#include "moc_.+/d' qtpositioning/src/plugins/position/geoclue2/qgeopositioninfosourcefactory_geoclue2.cpp qtpositioning/src/plugins/position/geoclue2/qgeopositioninfosource_geoclue2.cpp + ```