Files
dotfiles/memo/lfs.md
2025-12-31 00:08:10 +01:00

25 KiB
Raw Blame History

记录一些在安装 LFS 时踩的一些坑和其他值得一提的事情。

Important

本文中的 LFS 仅指代 12.4 SysV 版本

LFS

以下内容按照对应章节划分:

  • 2.4. Creating a New Partition

    虽然此处列举了很多分区,例如 /opt/usr/sources 等,但我并不喜欢,也并不觉得有必要将根目录分得如此细碎。对于 UEFI 引导的系统来说btw 我不认为 BIOS 引导和 UEFI 引导的配置难度之间的差距有很多观点说得那样巨大)除去可选的 Swap 分区外通常 //boot/boot/efi/home 几个分区就足够了。

  • 2.6. Setting the $LFS Variable and the Umask

    $LFS 变量很重要!建议写到 Host 的 /root/.bash_profile/etc/profile.d/... 或类似作用的配置文件里防止忘记设置。

  • 2.7. Mounting the New Partition

    可以用一个小脚本挂载分区,推荐使用文件系统 UUID 而非诸如 /dev/sdc1 这样的绝对路径:

    #!/bin/bash
    
    [ -z "$LFS" ] && exit 1
    
    set -euo pipefail
    
    # 替换为实际的 UUID
    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. Packages and Patches

    强烈建议通过镜像站下载打包好的所有源代码和 patch否则某些源服务器的下载速度即便在非受限的网络环境也很感人。

  • 4.5. About SBUs

    SBU 只提供大概的预期耗时范围,误差是很大的,尤其对于 BLFS 书中的一些编译时间很长的包(如 QtFirefox来说。

  • 5. Compiling a Cross-Toolchain

    从这一章开始将会手动编译大量的包,其中有不少操作是重复的,例如 tar -xfcdrm -rf 等,为此可以写一个小脚本节省时间:

    #!/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
    
    cleanup() {
        echo "Cleaning up..."
        cd ..
        if [[ -z "$dir" || "$dir" == "/" || "$dir" == "." ]]; then
            echo "Error: Unsafe directory for cleanup: $dir"
            exit 1
        fi
        rm -rf "$dir"
    }
    
    trap cleanup EXIT INT TERM
    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 退出时清理先前解压得到的文件。

    另外还有对于在 LFS 与 BLFS 中编译包的一些通用建议:

    1. 通常来说,应该(或者说请务必)在编译和安装一个包后完全删除它的目录,仅有少数例外:

      • linux保留构建树可以缩短重新构建耗时或至少保留 config 便于复原配置)
      • blfs-bootscripts在 BLFS 中)

      注意一些需要多次编译的包,例如 gcc也应该在每次编译与安装后完全删除目录。

    2. 通常来说,建议将所有相关的 tarball 和 patch 下载在同一个目录中,并且将 tarball 也解压在这个目录中。如果目录层级与该默认情况不一致的话必须修改 LFS 书中提供的命令中对应的相对路径。

    3. 如果和我一样使用 UEFI 引导,那么大概率将会在 8.64. GRUB-2.12 第一次接触 BLFS。和 LFS 不同BLFS 中大多数包都是可选的,一个包可能会依赖其他包,这些依赖分为三个层级:

      • Required

      • Recommended

      • Optional

        其中 Required 是必要的依赖;Recommended 通常建议当成 Required 看待,除非明确知道对应包的用途和不安装它的影响;Optional 酌情安装,很多时候是文档相关的依赖。

        更多关于安装 UEFI 版 GRUB 的话题将会在之后的章节中讨论。

    4. 如果想要的包在 LFS 和 BLFS 包中都没有,例如 fish、libglvnd、flatpak 等,不妨先检查 GLFSSLFS 或同系列的其他书中是否有涉及,如果有的话将节约很多学习和试错成本。

  • 7.4. Entering the Chroot Environment

    对于 chroot 也可以使用一个小脚本显著地改善体验:

    #!/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 时自动清理。

Important

此处并不建议使用 arch-install-scripts 提供的 arch-chroot 偷懒,除非明确知道自己在做什么。

Important

不同于常见的发行版LFS 需要在 /etc/fstab 中指定一系列虚拟文件系统,如果不这样做的话会导致无法启动。

  • 10.3. Linux-6.16.1

    内核配置是整本 LFS 书中最具有挑战的环节。对此我可以总结出几点建议:

    • 复刻并裁剪现有配置

      如果将要使用正在构建的 LFS 系统的机器和 Host 完全相同,并且内核版本相同或相近,可以将 Host 现在运行的内核的配置文件搬过来,同时仅启用当前 Host 加载的内核模块,这将极大地减小配置难度和缩短构建耗时:

      在 Host 上运行:

      # 进入内核源码目录
      cd /path/to/lfs/sources/linux-6.16.1
      
      # 导出当前内核配置(如果 Host 有 /proc/config.gz
      zcat /proc/config.gz > .config
      

      在 chroot 环境中运行:

      # 进入内核源码目录
      cd /path/to/lfs/sources/linux-6.16.1
      
      # 裁剪配置
      make localmodconfig
      

      如果内核版本不一致,会在 make localmodconfig 时出现很多交互选项,建议全部保持默认,或使用 make olddefconfig 来自动处理。

      在此之后,仍建议按照 LFS 书中的指示检查和调整配置选项。

    • 参考 Host 的内核配置

      即使不打算直接使用 Host 的内核配置Host 加载的内核模块也能为 LFS 内核的配置提供很好的参考,例如当一个门类下有很多并列且可选的选项时,可以优先启用 Host 当前加载的模块对应的选项。

    • BLFS 要求的内核配置

      除 LFS 书中列出的内核配置选项外BLFS 中单独列出了所有涉及的包需要的内核配置选项,建议在配置内核时参考 BLFS Index of Kernel Settings 一节,确保启用了所有必要的选项,否则后续可能需要多次重新编译内核。

    • 对于 NVIDIA

      如果之后会安装闭源的 NVIDIA 驱动程序,则不建议在内核中启用 nouveau 驱动,因为这会导致闭源驱动无法正常工作,后续禁用 nouveau 将会是额外的工作量。

    • <*> or <M>

      对于大多数选项建议选择模块化M而非内置*)。这将显著减少内核体积,并且提高灵活性。但有几种情况例外,例如:

      • 引导相关的选项(例如 EFI 支持)必须内置,否则无法引导。
      • 一些必要的文件系统(例如 ext4建议内置否则可能无法挂载根文件系统。

      也有必须模块化的情况,例如:

      • 需要固件支持的驱动程序(如 i915除非使用 initrd 或将固件内置到内核中。
  • 10.4. Using GRUB to Set Up the Boot Process

    强烈建议使用 PARTUUID 和 UUID 替代传统的 /dev/sdXN 设备路径以及 (hdM,N) 来指定 /boot 分区和根分区。

    另外,如果将外置存储设备(如 USB 硬盘)作为根文件系统,建议在 GRUB 配置中添加 rootdelay=10rootwait 参数以防止启动时找不到根文件系统。

Note

UUID 和 PARTUUID 可以通过 lsblk -o NAME,FSTYPE,UUID,PARTUUID 查看。

Note

UUID 指的是文件系统 UUIDGRUB 需要具备读取对应文件系统的能力才能识别 UUID。而 PARTUUID 指的是分区 UUID不依赖文件系统。

BLFS

绝大多数的建议都已在 LFS 部分提及,这里只做少数补充:

  • 阅读顺序

    BLFS 并不像 LFS 那样有线性的章节顺序,但仍建议先顺序阅读直到 After LFS Configuration Issues 章节结束再按自己的需要安装各种包。

  • About Firmware

    一个偷懒的方法是把 Host 的 /lib/firmware 目录复制到 LFS 的对应目录下:

    cp -av /lib/firmware $LFS/lib/
    

    其中 -a 选项会保留符号链接和文件权限等信息。

  • Mesa-25.1.8

    此处记录使用 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
      • 不包含 NVIDIA 相关的参数,因为 NVIDIA 专有驱动自带完整的 OpenGL 支持,不需要 Mesa 提供。
      • 同时启用 llvmpipe 用于 OpenGL 上下文中的软件渲染以防万一。
      • iris 用于 Intel 显卡。对于较新Gen 8 及更新)的硬件,crocus(适用于 Gen 4 到 Gen 7i915(更老)用户态 OpenGL 驱动已被废弃,不应再使用。注意此处的 i915 和内核中的 i915 内核驱动是不同的东西。
    • -D vulkan-drivers=intel,swrast
      • 启用 Intel iGPU 的 Vulkan 支持。
      • 同时启用 Vulkan 上下文中的软件光栅化驱动 swrast 以防万一。
    • -D glvnd=enabled:启用 GLVND 支持以便和 NVIDIA 专有驱动兼容。libglvnd 需要在 GLFS 书中安装
  • Qt-6.9.2

    此版本的 Qt 在构建时可能会出现错误,日志的一部分如下:

    错误日志
    [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 文件,和自动生成的元对象代码冲突,导致重复定义。出问题的源文件有两个,可以通过以下命令修复:

    sed -i -E 's|\s*#include "moc_.*|// &|g' qtpositioning/src/plugins/position/geoclue2/qgeopositioninfosourcefactory_geoclue2.cpp qtpositioning/src/plugins/position/geoclue2/qgeopositioninfosource_geoclue2.cpp