编译 QGIS,说难也难,说简单也简单。难在依赖多、版本杂、报错信息往往不直白;简单在于一旦环境理顺,剩下就是等进度条。我自己在 Windows 10 上用 MSVC 编译 QGIS 3.34.10 的整个过程前后折腾了两天,踩了不少坑,也摸出了一套可以稳定复现的流程。这篇就把完整的思路、命令、参数和排查经验整理出来,给想编译 QGIS 3.34 系列 LTR 版本的朋友做一个可以直接照抄的参考。
QGIS 3.34 是长期支持版本,官方安装包用起来很方便,但如果你要改 C++ 源码、做二次开发、定制插件,或者想在离线环境里部署自己的构建产物,源码编译就是绕不开的一步。这篇文章适合有一定 C++ 和 CMake 基础、想在 Windows 上拿到自定义 QGIS 可执行文件的开发者,也适合刚接触 GIS 二次开发、但愿意按步骤折腾一遍的人。整个流程会涉及 Visual Studio、OSGeo4W、CMake、Ninja、Qt、Python 这些组件,我会把每一步为什么要这么做也讲清楚。
1. 编译前的整体思路与方案选型
1.1 为什么锁定 MSVC 而不是 MinGW
很多从 Linux 过来的朋友第一反应是用 MinGW 或 MSYS2 编译,毕竟命令行体验更像 Unix。但 QGIS 官方发布的 Windows 包是 MSVC 构建的,生态里大量第三方插件、Python 绑定和扩展库也都按 MSVC ABI 编译。ABI 不一致的库硬凑到一起,轻则链接报一堆 undefined reference,重则运行时直接崩溃。
我用 MinGW 试过一次,编译本身能过,但运行起来时不时出现奇怪的符号冲突,尤其是接入 QGIS 的 C++ 插件时,加载一个基于 MSVC ABI 编译的插件就会直接报版本不匹配。所以结论很明确:Windows 上老老实实用 MSVC,Visual Studio 装好,一条路走到黑,这是官方支持也是社区验证最多的路径。
1.2 依赖管理的三条主流路线
QGIS 的依赖非常多,光核心库就有 GDAL、GEOS、PROJ、SQLite、Expat、libzip、OpenSSL、QCA、QScintilla、Qt5 等等。把这些依赖全部自己从源码编译一遍,工作量巨大,不现实。Windows 上通常有三条路:
| 依赖方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| OSGeo4W 全家桶 | 依赖版本统一、GIS 库齐全、与官方构建最接近 | 包名繁琐、默认带着自己的 Python/Qt 环境 | 绝大多数本地开发者 |
| QGIS-DEPS 预编译包 | 自包含、无需折腾包管理、下载即用 | 版本固定、升级不方便、排错时黑盒 | CI/CD 流水线、快速验证 |
| vcpkg / 自己编依赖 | 完全可控、可定制 | 需要编译 GDAL/GEOS 等,耗时且维护成本高 | 对依赖有特殊要求的场景 |
我这次选的是第一条路,也就是 OSGeo4W 全家桶。原因是它和官方 Windows 安装包的依赖来源一致,GDAL、GEOS、PROJ 这些地理库的版本都经过 QGIS 团队验证,版本冲突的概率最低。接下来提到的所有配置都以 OSGeo4W 为主线,如果你走 QGIS-DEPS 或者 vcpkg,思路类似,只是 CMAKE_PREFIX_PATH 指向不同目录。
1.3 版本匹配:3.34.10 的硬性约束
QGIS 3.34.10 作为 LTR 版本,对编译器、Qt、Python 都有明确要求。别指望随便装个版本就能编译过,我整理了一份实测可用的版本组合:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Visual Studio | 2019 / 2022,64 位 | 需要“使用 C++ 的桌面开发”工作负载 |
| Qt | 5.15.x | 3.34 系列还没切换到 Qt6,别装 Qt6 |
| Python | 3.9 ~ 3.11 | 建议跟着 OSGeo4W 的 python3-core 版本走 |
| GDAL | 3.x | OSGeo4W 默认版本即可 |
| GEOS | 3.10+ | 同上 |
| PROJ | 8.x / 9.x | 同上 |
| CMake | 3.24+ | 太老版本不识别一些新选项 |
| Ninja | 1.10+ | 比 jom 好用,报错信息直观 |
版本组合这块,最容易翻车的点是 Qt 和 Python。Qt 必须是 MSVC 2019 对应的构建,Python 必须和你要用的 PyQt5/sip 版本兼容。我建议 Python 用 3.10 或 3.11,太新反而容易遇到 sip 兼容性问题。
2. 环境准备与工具链安装
2.1 Visual Studio 2019 安装与验证
Visual Studio 是 MSVC 编译器的载体,安装时不需要全部组件,只勾选“使用 C++ 的桌面开发”这一项工作负载,然后在右侧的“单个组件”里确认 Windows 10 SDK 被勾上。如果你磁盘空间紧张,这样安装大概占用 10GB 左右,编译 QGIS 完全够用。
安装完成后,建议先在命令行里验证编译器可用。打开“Developer PowerShell for VS 2019”或普通 CMD,执行:
cl如果出现“Microsoft (R) C/C++ Optimizing Compiler”的版本信息,说明 MSVC 环境正常。注意,普通 CMD 里直接敲cl是不行的,必须先加载 vcvars64.bat 环境脚本,这个后面会专门讲。
2.2 OSGeo4W 依赖包:少踩坑的安装方法
从 OSGeo4W 或 QGIS 官网下载 osgeo4w-setup.exe,建议下载 64 位版本。运行后选择“Advanced Install”,安装目录我建议设为C:\OSGeo4W64,不要用中文路径,也不要有空格,这是给后续 CMake 省事。
包选择是重点。最省心的方式是在搜索框里搜 qgis,勾选qgis-rel-dev这个包,它会自动拉入一整套编译依赖。如果你更愿意手动控制,至少要确认以下几类包被选中:
- GIS 核心库:gdal、gdal-devel、geos、geos-devel、proj、proj-devel
- Qt 相关:qt5-qtbase、qt5-qtsvg、qt5-qttools 以及对应的 -devel 包
- 辅助库:qca-qt5、qca-qt5-devel、qscintilla-qt5-devel、qwt-qt5-devel
- 基础库:libzip、libzip-devel、zlib、zlib-devel、expat、expat-devel、sqlite3、sqlite3-devel
- 网络与数据库:openssl-devel、libcurl-devel、libpq-devel、libxml2-devel
看到带-devel的包就尽量勾上,devel 代表开发头文件和导入库,编译必需。整个下载安装过程会拉几百 MB 的文件,取决于网速,大概十几分钟到半小时。装完以后,C:\OSGeo4W64\include和C:\OSGeo4W64\lib里就有编译 QGIS 需要的全部头文件和导入库了。
2.3 Python / CMake / Ninja / Qt 的版本核对
Python 我建议直接用 OSGeo4W 环境里安装的版本。安装 OSGeo4W 时确认勾选python3-core和python3-devel,装完会在C:\OSGeo4W64\apps\Python310之类的目录下出现 Python 解释器。用它的最大好处是,sip 和 PyQt5 的头文件、库都由 OSGeo4W 统一提供,和 Python 编译器的版本天然配套,CMake 也不用额外折腾路径。
如果你更习惯用 python.org 的独立 Python,也可以,但那样就要自己 pip 安装 PyQt5 和 sip,并且需要显式把 PYTHON_INCLUDE_DIR、PYTHON_LIBRARY 传给 CMake,多一层工作量,第一遍编译不建议这么做。
CMake 和 Ninja 直接下载官方 Windows 安装包。CMake 安装时勾选“Add CMake to the system PATH”,Ninja 解压到C:\Tools\ninja后把该目录加到系统 PATH。检查环境:
cmake --version ninja --version python --version qmake -vqmake 来自 OSGeo4W 的 Qt5,命令位于C:\OSGeo4W64\bin\qmake.exe。如果 qmake 输出版本是 5.15.x,环境就算齐了。
3. 源码获取与 CMake 配置
3.1 源码下载与目录规划
QGIS 的源码从 GitHub 上获取,选 3.34.10 对应的 tag,分支名是final-3_34_10。可以用 git clone 指定分支,也可以直接下载对应发布版的 zip 包,两者本质一样。
我推荐的目录结构是:
C:\src\qgis-3.34.10 源码目录 C:\build\qgis-3.34.10-build 构建目录(out-of-source) C:\OSGeo4W64 依赖库目录 C:\QGIS-3.34.10-install 最终安装目录构建目录一定要放在源码目录外。QGIS 的 CMake 虽然允许 in-source 构建,但会污染源码树,后续增量编译和 git 操作都很痛苦。还有一点很重要,所有路径都别用中文和空格。CMake 和 MSVC 对带空格的路径处理时好时坏,遇到诡异报错很难排查,不如最开始就绕开。
磁盘空间预留 30GB 以上,其实纯构建产物大约 10GB,但各种缓存、中间文件和安装副本很容易超预期。
3.2 初始化命令行环境
编译 QGIS 需要在命令行完成,环境初始化顺序有讲究。打开 CMD,先加载 MSVC 环境,再把 OSGeo4W、CMake、Ninja 的路径追加到 PATH:
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" set PATH=C:\OSGeo4W64\bin;%PATH% set PATH=C:\OSGeo4W64\apps\Python310;%PATH% set PATH=C:\Program Files\CMake\bin;C:\Tools\ninja;%PATH%如果你用的是 VS2022,vcvars64.bat 路径改为C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat。
为什么先执行 vcvars64.bat?因为它会设置 cl.exe、rc.exe、link.exe 等一大堆 MSVC 工具链的环境变量,如果 PATH 里先混入了 OSGeo4W 自带的工具,有可能发生工具冲突。先把 MSVC 环境固化下来,再追加其他路径,最稳。
3.3 CMake 参数逐项解析与实测配置
在源码根目录下执行 CMake 配置。以下是我验证过的完整命令行:
cmake -S C:/src/qgis-3.34.10 -B C:/build/qgis-3.34.10-build -G Ninja ^ -DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_INSTALL_PREFIX=C:/QGIS-3.34.10-install ^ -DCMAKE_PREFIX_PATH="C:/OSGeo4W64;C:/OSGeo4W64/apps/Qt5" ^ -DCMAKE_INCLUDE_PATH=C:/OSGeo4W64/include ^ -DCMAKE_LIBRARY_PATH=C:/OSGeo4W64/lib ^ -DPYTHON_EXECUTABLE=C:/OSGeo4W64/apps/Python310/python.exe ^ -DWITH_BINDINGS=ON ^ -DWITH_3D=ON ^ -DWITH_GRASS=OFF ^ -DWITH_SERVER=OFF ^ -DENABLE_TESTS=OFF ^ -DWITH_APIDOC=OFF逐项说明这些参数为什么这样设:
CMAKE_BUILD_TYPE=Release:编译优化后的发行版,QGIS 官方包也是 Release。Debug 版可以编译,但你跑起来会明显感觉卡,而且体积巨大。CMAKE_INSTALL_PREFIX:最终 install 的安装目录,建议和源码、构建目录分开。CMAKE_PREFIX_PATH:告诉 CMake 去哪儿找依赖。这里同时包含 OSGeo4W 根目录和 Qt5 所在目录,两个路径都不能省。CMAKE_INCLUDE_PATH和CMAKE_LIBRARY_PATH:OSGeo4W 的头文件和库目录不是标准布局,CMake 有时搜不到,显式指定最保险。PYTHON_EXECUTABLE:指向 OSGeo4W 的 Python,保证 Python 绑定和 PyQt5/sip 出自同一套环境。WITH_BINDINGS=ON:开启 Python 绑定,也就是 PyQGIS。如果你不需要 Python 插件,可以 OFF,但建议 ON,QGIS 很多实用功能依赖 Python。WITH_3D=ON:QGIS 3D 视图功能,依赖 Qt3D,OSGeo4W 有对应库。如果编译报 Qt3D 找不到,可以改成 OFF。WITH_GRASS=OFF:GRASS 插件依赖的系统库太多,源码编译极其容易栽跟头。非 GIS 重型用户建议直接关掉。WITH_SERVER=OFF:QGIS Server 是服务端组件,桌面开发不需要就先关,能减少不少编译时间。ENABLE_TESTS=OFF:跳过测试目标,编译速度快很多。WITH_APIDOC=OFF:不生成 API 文档,省时省力。
如果 CMake 配置完成后没有报错,就会生成build.ninja文件。这一步如果报缺库缺头文件,绝大多数是 CMAKE_PREFIX_PATH 或 LIBRARY_PATH 没写对,回到 2.2 核对包是否装全。
还有一个细节:如果机器上同时装了 Qt6,CMake 有可能误找到 Qt6 的包导致配置失败。这时在 CMake 参数里加一行-DQT_VERSION_MAJOR=5强制锁定 Qt5,能省去很多麻烦。
4. 编译执行与产出物说明
4.1 触发构建:Ninja 与并行参数
配置成功后,执行编译:
cmake --build C:/build/qgis-3.34.10-build --parallel 8--parallel后面的数字是并行编译任务数,建议设为物理核心数或稍小一点。我的机器是 8 核 16 线程,设置 8 或 10 都稳定。如果内存只有 16GB,尽量别超过 8,否则每个编译进程吃几百 MB 内存,内存不够会触发 OOM,反而更慢。
首次完整编译时间大致如下:
| 机器配置 | 预计耗时 |
|---|---|
| 8 核 / 16GB 内存 / SSD | 1.5 ~ 2 小时 |
| 16 核 / 32GB 内存 / SSD | 30 ~ 45 分钟 |
| 8 核 / 16GB 内存 / 机械硬盘 | 3 小时以上 |
编译期间控制台会不断刷[xxx/xxxx] Building CXX object ...的进度信息,Ninja 还会显示当前构建的百分比。不要因为很久没动 QQ 消息就以为卡死了,编译 QGIS 大文件时单个目标几十秒都正常。
4.2 常用构建目标与增量编译
开发过程中没必要每次都全量编译。QGIS 的构建系统里几个常用目标分别是:
cmake --build C:/build/qgis-3.34.10-build --target qgis_core cmake --build C:/build/qgis-3.34.10-build --target qgis_app cmake --build C:/build/qgis-3.34.10-build --target qgis- qgis_core:核心库,也就是
qgis_core.lib和qgis_core.dll,QGIS 最底层的数据模型、地图渲染、空间分析都在这。 - qgis_app:应用程序层,包含主窗口和交互逻辑,产物是
qgis_app.lib。 - qgis:最终可执行目标,生成
qgis.exe。
Ninja 的增量编译做得很好,修改某个源文件后再次执行 build,它只会重新编译受影响的翻译单元,链接时也复用已有的 .obj 文件。我实测修改一个核心文件后增量编译只需要几分钟,这对迭代开发非常重要。
4.3 运行、安装与部署
编译完成后,可执行文件的位置取决于生成器。因为用的是 Ninja(单配置生成器),qgis.exe 生成在:
C:\build\qgis-3.34.10-build\output\bin\qgis.exe开发环境里直接运行会遇到 DLL 找不到的问题,需要把 Qt 和 OSGeo4W 的 bin 目录加进 PATH:
set PATH=C:\build\qgis-3.34.10-build\output\bin;%PATH% set PATH=C:\OSGeo4W64\bin;%PATH% qgis.exe如果准备把编译产物安装到独立目录:
cmake --install C:/build/qgis-3.34.10-build执行完以后,C 盘 QGIS-3.34.10-install 目录下会有完整的 bin、lib、share、plugins 等目录。运行安装版 qgis.exe 时,还需要设置 QGIS_PREFIX_PATH 环境变量指向安装目录,否则程序找不到插件和资源文件:
set QGIS_PREFIX_PATH=C:\QGIS-3.34.10-install C:\QGIS-3.34.10-install\bin\qgis.exe真正要把这个编译结果部署到别的机器上,推荐两种方式。一种是直接把整个安装目录拷贝过去,同时确保目标机器有对应版本的 Visual C++ 运行库、OSGeo4W 运行库和 Qt 运行库。另一种是用 windeployqt 自动收集 Qt 依赖,再手动补充 GDAL、GEOS、PROJ 的 DLL。前者省事,后者干净,看你是自己用还是发给其他人。
5. 常见问题与排查技巧实录
5.1 高频报错速查表
编译 QGIS 的报错看起来千奇百怪,但排掉表象后其实都是几个原因。我整理了一份高频速查表:
| 报错信息 | 原因 | 处理方法 |
|---|---|---|
| Could not find a package configuration file provided by "Qt5" | CMake 找不到 Qt5 的 cmake 配置 | 检查 CMAKE_PREFIX_PATH 是否包含 C:/OSGeo4W64/apps/Qt5,或显式 -DQt5_DIR |
| Could NOT find GDAL (missing: GDAL_INCLUDE_DIR) | OSGeo4W 头文件路径没传给 CMake | 确认 gdal-devel 已装,检查 CMAKE_INCLUDE_PATH |
| No such file or directory: sip.h | Python 绑定需要 sip 头文件 | 确认 python3-devel 和 python3-sip 已安装,或关闭 WITH_BINDINGS |
| ninja: error: loading 'build.ninja' | CMake 配置失败或未完成 | 先回看 CMake 输出信息,修正参数后重新执行 cmake -S |
| LNK1104: cannot open file 'qgis_core.lib' | 链接时核心库还没生成 | 先编译 qgis_core 目标,再编译 qgis_app/qgis |
| Program can't start because Qt5Core.dll is missing | 运行时找不到 Qt DLL | 把 C:/OSGeo4W64/bin 加入 PATH |
| qgis.exe 闪退,但控制台无输出 | DLL 版本混用或插件目录异常 | 检查 PATH 中是否存在多处 Qt,统一为 OSGeo4W 的 Qt |
5.2 新手最容易踩的四个坑
先说第一个坑:Qt 和 OSGeo4W 混用。OSGeo4W 的 bin 目录里有一套 Qt 运行库,如果你又在系统里装了官方 Qt 5.15,并且 PATH 顺序不对,qgis.exe 启动时会加载到错误的 DLL,表现就是闪退没有任何提示,或者在启动日志里报一堆奇怪的 symbol 错误。解决办法就是统一来源,全部用 OSGeo4W 的 Qt,或者全部用官方 Qt + QGIS-DEPS 依赖,两者不要混。
第二个坑是 Python 绑定版本不一致。如果你的 WITH_BINDINGS 打开了,但 Python 用的是 python.org 版本,而 PyQt5/sip 来自 OSGeo4W,编译阶段可能侥幸通过,运行时导入 qgis 库十有八九报ModuleNotFoundError或 sip 版本不匹配。如果你不想用 OSGeo4W 的 Python,就老老实实pip install PyQt5==5.15.* sip,并把 CMake 的 PYTHON_LIBRARY 显式指过去。
第三个坑是 CMake 缓存残留。我第一次配置时 CMAKE_INCLUDE_PATH 写错了,编译报找不到 gdal.h,改掉后重新执行 CMake 配置,但忘了缓存里还有旧路径。结果是新的找不到和旧的残留共存,反复报错非常崩溃。遇到依赖路径变化的情况,直接把 build 目录删掉,重新配置,反而比来回试参数更快。删一个目录不过几分钟,浪费半小时排查缓存问题才是真亏。
第四个坑和杀毒软件有关。Windows Defender 实时扫描在编译大量小文件时会造成巨大性能损耗,我试过把 build 目录加入排除列表后,编译时间直接缩短三分之一。如果编译过程中发现 CPU 占用忽高忽低但进度很慢,可以检查一下是不是杀毒软件在做文件扫描。
5.3 编译完成后的功能验证清单
编译成功不意味着万事大吉,我习惯按下面的清单逐一验证:
- 启动 qgis.exe,能正常显示主窗口,没有闪退。
- 菜单“帮助 -> 关于 QGIS”里显示的版本号是 3.34.10,且“已安装的库”里能看到自己编译时间相关的信息。
- 拖入一个本地 shapefile 或 GeoPackage 文件,地图画布能正常显示要素。
- 打开 Python 控制台,输入
import qgis.core,没有报错。 - 加载一个 XYZ 瓦片底图(比如 OpenStreetMap),确认网络、Qt 网络模块、栅格渲染链路正常。
- 随手跑一个“重投影”或“裁剪”算法,确认 GDAL、PROJ 库工作正常。
前两项能过,说明核心编译成功;Python 那项能过,说明绑定没有问题;后两项能过,说明依赖库在运行时都配对上了。如果最后发现地图渲染时某些图标缺失或者工具按钮异常,大概率是资源文件路径没找到,先检查 QGIS_PREFIX_PATH 是否设置正确,而不是怀疑编译有问题。
我个人在实际操作中的体会是,QGIS 源码编译在 Windows 上并不神秘,本质就是依赖版本统一、路径不要有中文或空格、缓存出问题果断重置这三件事。如果你首次配置 CMake 报错,沉住气,把报错信息里提到的库名和“missing”放一起搜,多半是某个 -devel 包没装。最后再分享一个小技巧:编译过程中如果想暂时退出,直接关掉 CMD 窗口即可,Ninja 会把已经完成的编译结果存在 build 目录里,下次重新跑同一条 build 命令,它会自动跳过未变更的源文件,继续从断点前进。这个特性在你需要反复调整源码时特别友好。