
1. 为什么要自己动手编译一次 QGIS 3.34.10在 Windows 10 上把 QGIS 3.34.10 从源码编译出来这事做之前看起来简单做的时候才发现坑全在细节里。绝大多数人日常用 QGIS都是去官网下载一个 exe双击装完直接开用没必要碰源码。但如果你开始关注插件开发、C 二次开发或者想要长期依赖一个不被别人编译参数左右、自己能完全把控的版本那源码编译就是绕不开的一步。QGIS 3.34 属于长期支持版LTR意味着官方维护周期更长、发布更克制适合企业级项目和生产环境。3.34.10 是这一系列里的一个补丁版本修复了不少稳定性问题。其实官方也提供 OSGeo4W 这种包管理器勾选安装之后同样能拿到相对完整的开发环境但“能用”和“自己能完整编译一遍”是两回事。自己编译一次至少能确定几件事依赖栈是怎么串起来的、哪些第三方库参与了构建、CMake 配置里每个开关到底影响什么以及出了问题能否从源码层面定位。我自己的感受是Windows 平台编译 QGIS 比 Linux 麻烦但远没有想象中那么可怕。核心难点不在 C 编译本身而是在于依赖的版本匹配和工具链一致性。只要把这三件事理顺MSVC 编译器、OSGeo4W 依赖栈、CMake 配置整个流程就能跑通。不夸张地说这才是了解 QGIS 架构的“最短路”。这篇记录面向的读者有两类一类是想给 QGIS 扩展自定义模块需要从源码构建开发环境的开发者另一类是刚接触地理信息软件的运维或测试人员想验证官方 LTR 版本的源码可靠性。文中会把我在实际构建过程中的完整步骤、参数选择、报错排查都写出来尽量让第一次接触的人也能顺着路径复现。2. 环境准备与工具链选型2.1 编译器选择尽量别混用 MinGW 和 MSVCQGIS 在 Windows 上官方推荐的构建方式是 MSVC 配合 OSGeo4W 依赖库不建议混用不同编译器的产物。原因是 C 的 ABI 在不同编译器之间不兼容OSGeo4W 里的第三方库大多是按 MSVC 或 MinGW 分别发布的混着链接会出现形形色色的链接错误比如找不到符号、运行时崩溃、内存布局不一致。所以我的建议是直接用 Visual Studio 2019 或 2022 的 MSVC 编译器配上 Ninja 构建系统。不用打开 Visual Studio 的完整 IDE只需要装好“使用 C 的桌面开发”工作负载让系统里有 cl.exe 和 Windows SDK 即可。QGIS 3.34 的代码基于 C17对编译器版本有一定要求MSVC 2019 16.x 以上都行我用 VS2019 实测没有问题。如果你机器上同时装了 MinGW、MSYS2 或 Cygwin构建前最好确认 PATH 环境变量里没有被它们先入为主。特别是 OSGeo4W 的 shell 里某些辅助工具会依赖 Unix 命令但交叉编译环境很容易污染 CMake 的探测结果。我在最开始就吃过这个亏cmake 探测时找错了编译器中途立刻停止重来也算少走了一段弯路。2.2 OSGeo4W 依赖栈准备QGIS 不是一个人能轻松从头编译的纯单体软件它依赖 GDAL、PROJ、GEOS、Qt 5、QScintilla、Expat、SQLite3、GSL 等一批地理空间和界面库。Windows 上没有各发行版的软件仓库那么方便最可靠的办法就是安装 OSGeo4W从它提供的包统一取依赖。OSGeo4W 的安装器界面看起来有点老但完全可以接受。建议选择“高级安装”在“选择包”阶段切换到“完整”或“自定义”然后勾选我下面列出的这些包分类包名作用基础工具cmake, ninja, bison, flex构建系统生成和语法扫描核心库gdal-dev, proj-dev, geos-dev空间数据读写、投影、几何运算Qt 相关qt5-dev, qscintilla-dev, qwt-dev界面框架和控件扩展其他库gsl-devel, expat-devel, sqlite3-devel, exiv2-dev数学计算、解析 XML、数据库、图像元数据Pythonpython3-dev可选绑定插件和 PyQGIS 需要这里要特别提醒QGIS 编译时需要的包大多是-dev或-devel结尾的开发版本光装运行时库是不够的。如果你在 CMake 阶段发现某个依赖找不到第一反应不是去网上到处找 DLL而是回到 OSGeo4W 的包管理器里检查是否漏装了对应的开发包。OSGeo4W 默认安装目录建议使用C:\OSGeo4W或C:\OSGeo4W64。越简单的路径越安全因为源码构建过程中会有大量脚本拼接路径目录一旦带空格或中文很容易让某些工具解析出错。这一点在后面 CMake 配置时还会再次踩到。2.3 源码目录与公共环境变量QGIS 源码可以放在任意目录但我推荐建设一个专门的工作目录比如D:\Work\qgis-build里面分成source和build两个子目录。source 存放源码build 存放构建产物。好处是日后升级源码或彻底重编时可以清楚地知道谁是谁不会在多个build目录里迷路。在开始所有操作前需要确保 PATH 环境变量里能看到关键工具的入口。通常我会打开 OSGeo4W 自带的命令行外壳因为它会替你把 Qt、GDAL、PROJ 等目录挂进环境变量。然后再手动确认where qmake where cmake where ninja where g对 Windows 上的编译来说qmake必须指向 Qt5 版本。QGIS 3.34 系列还在使用 Qt 5.15而不是 Qt 6。不要因为机器上装了新版 Qt 就顺手把 qmake 指向它否则 CMake 探测 Qt 版本时会直接判定失败。另外建议在系统环境变量里增加一条OSGEO4W_ROOT指向你的 OSGeo4W 根目录。很多 QGIS 脚本和 CMake 模块会通过这个变量去定位依赖。虽然它不是 QGIS 编译的强制要求但设置后可以减少自己对路径的猜测。3. CMake 配置与构建参数3.1 拉取源码和子模块源码获取建议直接使用 Git从 QGIS 官方仓库拉取对应标签。git clone --depth 1 --branch final-3_34_10 https://github.com/qgis/QGIS.git source cd source这里我使用 shallow clone 可以避免拉取整个历史记录节省不少时间和磁盘空间。但有一点要注意QGIS 仓库里包含一些需要子模块的内容直接浅克隆可能不会把子模块一并拉下来。稳妥起见进入source目录后再执行一次git submodule update --init --recursive由于仓库已经切换到最终版本 tag子模块也会指向对应版本的提交基本不会出现漂移问题。如果是在国内网络环境下拉取 GitHub可以考虑用镜像或代理把速度提上来否则某些大文件会等得让人怀疑人生。成功之后可以在CMakeLists.txt里看一眼PROJECT_VERSION_PATCH这类定义确认版本是 10避免拉错分支。这一步是廉价保险值得养成习惯。3.2 CMake 配置阶段最容易忽略的选项CMake 配置是整个编译过程里最需要耐心的一步。我建议把生成的构建系统放到外置目录也就是源码目录外的build目录这样不会污染源码。下面给出我实际使用的配置命令cmake -S . -B ../build \ -G Ninja \ -DCMAKE_BUILD_TYPERelease \ -DCMAKE_PREFIX_PATHC:/OSGeo4W \ -DCMAKE_INSTALL_PREFIXC:/QGIS \ -DWITH_BINDINGSON \ -DWITH_3DON \ -DWITH_QT5ON \ -DWITH_GUION \ -DWITH_SERVERON \ -DWITH_QUICKOFF逐个解释一下这里的参数套路。-DCMAKE_PREFIX_PATH很关键。它告诉 CMake 去哪找 Qt、GDAL、PROJ 等第三方库。OSGeo4W 的目录结构是apps/qt5、apps/gdal、lib、bin这种布局CMake 拿到前缀之后会自动推算头文件和库文件的位置。-DCMAKE_INSTALL_PREFIX决定最终ninja install时把 QGIS 安装到哪个目录。我习惯单独放到C:\QGIS而不是直接写到 OSGeo4W 内部这样卸载和切换版本都清晰。WITH_QUICKOFF是把 Qt Quick 相关的组件关掉。除非你要做 QGIS 移动端或嵌入式界面调试否则初期不需要能省掉一大串 Qt 模块依赖。WITH_3DON和WITH_SERVERON则根据自己需要处理3D 是桌面端地图分析常用服务端则适合后续做 GIS 服务调试。需要注意CMake 配置文件非常多QGIS 自己还支持QGIS_INSTALL_DATADIR、PYPYSDK、GRASS等一百多个选项。第一次编译不要试图全开先把核心和基本界面跑通后面需要什么再增量开启。3.3 生成构建系统并完成首次配置CMake 配置成功时会打印出一大堆依赖摘要里面会清楚显示 Qt 版本、GDAL 版本、PROJ 版本、Python 版本等。这一步请务必截图或仔细读一遍凡是显示NOTFOUND的项目都要警惕。如果某个依赖找不到最常见的处理方法是回到 OSGeo4W 补装开发包然后重新配置。很多人喜欢直接删掉整个 build 目录重来其实 CMake 有缓存机制重新运行刚才的命令时会复用已有结果不会每次重头探测。真正需要删除 build 目录的情况是我改了CMakeLists.txt或切换了编译器等重大变更时。我遇到过一次比较隐蔽的问题CMake 探测到了系统自带的 Python而没有探测 OSGeo4W 的 Python导致后续 PyQGIS 绑定阶段出现版本错配。解决办法是在配置命令里显式指定-DPYTHON_EXECUTABLEC:/OSGeo4W/apps/Python312/python.exe至于 Python 具体版本号要用你自己环境里的实际目录。这里没有统一标准取决于 OSGeo4W 当前仓库里提供哪个版本。4. 开始编译从首次执行到顺利出产4.1 使用 Ninja 完成一次完整编译配置通过后进入 build 目录直接执行编译cd build ninjaNinja 会根据 CMake 生成的build.ninja文件安排并行任务默认会尽量吃满 CPU 线程。如果你的机器核心数比较多第一次全量编译可能只需要十分钟到半小时如果核心较少或者内存不足则可能非常痛苦。我在 8 核心 16 线程、32G 内存的 Windows 10 机器上完整编译 Release 版大约花了 20 分钟。内存占用峰值接近 8G还算温和。如果你的机器只有 16G 内存可以限制并行度避免编译时整机卡死ninja -j 8不要一上来就ninja -j 32Windows 下单个 C 文件的编译内存消耗并不算小特别是 QGIS 里某些大型模板头文件编译起来非常吃内存。限制并行度虽然会让耗时变长但至少不会中途因为内存不足而出错。编译过程中会先生成大量第三方依赖的状态检查然后是核心库、GUI 库、分析库、Python 绑定最后才生成可执行文件。看到qgis-bin.exe链接成功基本就能确认大功告成了。4.2 编译期报错典型现象和解法在 Windows 10 上编译 QGIS遇到报错才是常态关键是别慌。我整理了几类最常见的错误和解决思路基本覆盖了大多数初次编译场景。错误现象可能原因处理方式Could not find Qt5qmake 路径不对或 CMAKE_PREFIX_PATH 没指到 OSGeo4W检查 Qt 5 的 qmake 位置重新配置 CMakeCould NOT find PROJOSGeo4W 里缺少 proj-dev 包回到 OSGeo4W 安装 proj-devXXX.lib not found链接时找不到第三方库确认对应 dev 包是否安装完整unresolved external symbol编译器不匹配或 x64/x86 混乱保证用的是 64 位 MSVC 工具链LNK1104 cannot open file杀毒软件锁定可执行文件关闭实时防护或把构建目录加入白名单编译内存不足并行任务过多调低ninja -j并行数有一个特别容易被忽略的问题是杀毒软件。Windows Defender 会在编译时实时扫描临时目录里的 DLL 和 exe导致链接器写入文件失败报出莫名的LNK1104或文件占用错误。这是我掉了好多次坑才意识到的把build目录加入 Windows Defender 的排除目录后编译速度甚至都有肉眼可见的提升。另外一个经典问题是磁盘空间。QGIS 编译产物和依赖文件都很大源文件加构建目录通常要预留 20GB 以上。如果系统盘空间紧张我建议把source和build都放在剩余空间多的非系统盘。4.3 两次编译之间如何正确增量更新如果你拿到了更新一点的 3.34.x 版本想在已有 build 目录上增量编译需要注意几件事。首先在源码目录执行git pull时要确认有没有子模块变动。如果有需要重新git submodule update --init --recursive。其次增量编译前不一定需要重新配置 CMake直接ninja会自动检测变化的文件和重新生成构建设置。只有当 CMakeLists.txt 或源码结构发生重大变化时才需要手动重新运行一次 CMake 配置。我建议在每次编译后把关键信息记录到一个简单备忘里包括用到的 CMake 参数、OSGeo4W 包的版本号、编译成功的时间点。这样万一某天编译失败可以对着记录判断是依赖更新导致还是源码变更导致排查效率能高很多。5. 安装部署与启动验证5.1 安装到指定目录编译完成之后使用安装目标把所有产物复制到CMAKE_INSTALL_PREFIX指定的目录ninja install这步会把qgis-bin.exe、相关 DLL、插件目录、Python 绑定、资源文件等全部部署到C:\QGIS。你可以直接把这个目录当作一个便携版 QGIS 使用也可以把整个目录套上新的名字用于不同版本并存。有一点必须强调不要直接拷贝单个qgis-bin.exe到其他机器使用。QGIS 运行依赖一堆第三方 DLL安装目标里虽然复制了大部分但仍有一部分依赖在 OSGeo4W 的环境目录中。便携化部署可以之后再做前提是把依赖关系理清楚否则会变成 DLL 地狱。5.2 启动脚本与外部依赖处理安装完成后直接双击qgis-bin.exe可能会白屏或提示缺少 DLL原因在于它需要运行时找到 Qt5 的插件目录和 OSGeo4W 的 Python 环境。最省事的做法是创建一个批处理启动脚本start-qgis.batecho off set OSGEO4W_ROOTC:\OSGeo4W call %OSGEO4W_ROOT%\bin\o4w_env.bat set PATHC:\QGIS;%PATH% C:\QGIS\bin\qgis-bin.exeo4w_env.bat是 OSGeo4W 自带的环境初始化脚本能把 Qt、GDAL、Python 的路径挂到当前进程里。这样做的好处是干净只影响这个脚本所在的命令行窗口不会污染系统环境变量。如果你的 QGIS 安装目录里的bin下能找到大部分 DLL但还是提示找不到某个特定文件可以直接到OSGeo4W\bin目录下去找同名 DLL复制到 QGIS 的bin目录即可。但在复制之前要确认这个 DLL 是 64 位版本否则架构不匹配会导致加载失败。5.3 快速验证自编译版本验证编译结果最直接的方式是进入 QGIS 主界面打开“帮助”菜单点击“关于”查看版本号是否显示 3.34.10。更好玩的是源码编译版在关于信息里通常会包含构建时间、编译器、C 配置等元数据和官方安装包相比多了不少说明信息。如果想进一步验证核心库是否正常可以打开 Python 控制台尝试import qgis.core from qgis.core import QgsApplication, QgsCoordinateReferenceSystem crs QgsCoordinateReferenceSystem(EPSG:4326) print(crs.description())如果能正常输出 WGS84 描述文本说明核心库、Python 绑定和坐标参考框架初始化都没有问题。这一步比单纯看启动画面靠谱得多。随后可以打开一个矢量或栅格数据做基本的加载测试。我习惯随手打开一两个行政区划 Shapefile 和一张遥感影像分别做一次简单缩放漫游再用“属性表”打开统计字段。如果这几个操作都不卡不崩溃说明 GUI 和渲染主链路基本正常。6. 编译完成之后还能做什么6.1 基于源码版本做二次开发自编译版本最大的优势在于二次开发。你可以新建一个 C 插件项目链接到编译产物里的qgis_core.lib和qgis_gui.lib然后在 CMake 里把自己的项目指到 QGIS 安装目录。写插件时用实际编译出来的头文件和库文件能最大程度避免“官方 SDK 和自己代码版本不一致”带来的诡异崩溃。如果你更感兴趣 Python 插件源码构建后的 PyQGIS 模块可以直接被外部解释器调用。只要环境变量里能正确挂载C:\QGIS\apps\qgis\python路径你就不必非要依赖 QGIS 内置控制台可以用自己熟悉的 IDE 做单步调试。6.2 调试版本与性能分析的建议日常使用建议编译 Release 版本启动速度和内存占用都更友好。但如果是定位崩溃问题或排查渲染 bug可以考虑再单独配置一个 Debug 或 RelWithDebInfo 构建目录。只要把-DCMAKE_BUILD_TYPEDebug替换上去重新走一遍编译即可。Debug 版本编译时间会明显变长运行速度也慢不少不建议当作默认工作版本。我自己的做法是Release 目录长期保留作为日常使用Debug 目录只在使用调试器追踪具体问题时才编译。另外如果机器内存比较宽裕可以开启WITH_SERVERON和WITH_3DON编译一个完整版之后就能在本地起一个 QGIS Server 实例用 WMS/WFS 接口做内网地图服务联调。这样既能验证桌面端又能覆盖服务端场景一套源码吃满两个方向。7. 给后来者的一些心里话把 Windows 10 上编译 QGIS 3.34.10 的整个过程复盘下来我发现最容易让人半途放弃的并不是编译代码本身而是“工具链一致性”这个概念。Windows 平台上缺包、版本错、架构混用都会产生让你摸不着头脑的错误。但只要能把 OSGeo4W、MSVC、Qt 5 三者锁定在同一套环境下剩下的问题基本都能按图索骥查出来。我踩过最大的坑就是一开始图省事直接用 Visual Studio 的 CMake 配置生成 MSBuild 工程。结果项目文件巨大整体编译资源管理很差还经常出现莫名其妙的源文件依赖顺序问题。后来换成 Ninja 作为生成器速度和自动化程度都提高了一个档次所以强烈建议新手直接走 Ninja 路线。另一点心得是不要害怕删build目录。很多时候你改了某个 CMake 选项但缓存里还留着旧值后果比删掉重来更糟。与其在缓存里找问题不如保留源码、重新生成构建成功概率反而更高。以后再做与 QGIS 相关的环境测试我会直接把这套完整构建过程固化成一个脚本。先让机器自动初始化 OSGeo4W 依赖再通过 CMake 脚本生成配置最后用 Ninja 完成构建。只要依赖版本不变整个流程是可以做到一键化的。