PROJ 9.1.1源码编译实战:Win10+VS2022环境配置与疑难解决
1. 从零开始:认识PROJ与编译前的准备
如果你正在捣鼓地理信息相关的项目,大概率会碰到一个叫PROJ的库。简单来说,PROJ就是地图投影领域的“翻译官”。我们地球是个球体,但地图是平的,把球面坐标转换成平面坐标,或者在不同平面坐标系之间转换,就是PROJ最拿手的事。它不是什么带界面的软件,而是一个功能强大、被无数GIS软件(比如QGIS)和开源库(比如GDAL)作为核心依赖的C/C++库。很多朋友在Windows上用现成的安装包,但当你需要最新特性、特定修改,或者想深入理解底层时,自己动手从源码编译就成了必经之路。这次,我就带你用Win10和最新的Visual Studio 2022,把PROJ 9.1.1的源码变成你项目里能调用的库。
自己编译听起来有点吓人,但其实就像按菜谱做一道复杂的菜,只要材料(工具)备齐,步骤清晰,耐心操作,成功率非常高。最大的好处是“可控”。你可以决定编译成32位还是64位,是静态库还是动态库,是否开启某些特定功能(比如网络网格偏移支持),还能在调试时深入库的内部。我最初也是为了调试一个坐标转换的诡异边界问题,才下定决心自己编译,一路踩坑过来,积累的经验今天全部分享给你。
在动手之前,我们得把“厨房”收拾好。你需要准备以下几样东西,缺一不可:
- Windows 10操作系统:这个大家应该都有,确保系统更新到较新的版本,避免一些底层运行时库缺失。
- Visual Studio 2022:这是我们的主厨工具。社区版(免费)完全够用。安装时,记得勾选“使用C++的桌面开发”这个工作负载,里面包含了编译器、链接器和基本的Windows SDK。如果担心漏掉,可以把“MSVC v143 - VS 2022 C++ x64/x86生成工具”和“Windows 10/11 SDK”也选上。
- CMake:这是我们的“自动菜谱生成器”。PROJ的源码并不直接提供VS的工程文件(.sln),而是使用CMake这个跨平台的构建系统来生成。你需要去CMake官网下载安装包,安装时记得把“Add CMake to the system PATH for all users”选项勾上,这样在命令行里就能直接用了。
- PROJ 9.1.1源码:主食材。去PROJ的官方GitHub仓库(https://github.com/OSGeo/PROJ)找到Release页面,下载
proj-9.1.1.tar.gz这个源码压缩包。用你喜欢的解压工具(比如7-Zip)把它解压到一个路径不含中文和空格的目录,比如D:\Dev\proj-9.1.1。这是我的血泪教训,路径里有中文或空格,后续编译常常会报一些莫名其妙的错误。 - SQLite3库:PROJ需要用它来管理自己的数据库(比如投影参数、网格文件索引)。这是编译过程中最容易卡住的一环。你可以选择自己编译SQLite3,也可以使用预编译的二进制文件。为了省事,我推荐去SQLite官网下载“Precompiled Binaries for Windows”中的
sqlite-dll-win-x64-*.zip(64位)和sqlite-amalgamation-*.zip。前者包含DLL和DEF文件,后者是合并的C源码头文件。我们后续配置会用到。
准备好这些,我们的“编译厨房”就算搭建完毕了。接下来,就是最核心的环节:使用CMake生成VS2022工程。
2. CMake配置详解:一步步生成VS工程
很多新手败在这一步,主要是因为CMake的配置选项看起来有点复杂。别担心,我们一步步来,我不仅告诉你怎么做,还会解释为什么这么做。
首先,启动CMake GUI。它的界面主要分两部分:上半部分让你指定源码路径(Where is the source code)和构建路径(Where to build the binaries)。下半部分则是配置选项和日志输出区。
第一步,指定路径。点击“Browse Source...”,选择你解压好的PROJ源码目录,例如D:\Dev\proj-9.1.1。然后点击“Browse Build...”,这里需要你新建一个目录,用于存放CMake生成的所有中间文件和最终的VS工程。我习惯在源码同级目录下建一个build或vs2022文件夹,比如D:\Dev\proj-9.1.1\vs2022。这样做的好处是源码和构建文件分离,非常干净,想重新构建时直接删掉vs2022文件夹就行,不影响源码。
第二步,首次配置。点击左下角的“Configure”按钮。这时会弹出一个窗口让你选择“生成器(Generator)”。这就是决定CMake为谁生成工程文件的关键一步。在下拉列表中,选择“Visual Studio 17 2022”。右边还有一个“Optional platform for generator”选项,默认是空,这意味着生成器会使用默认平台(通常是x86)。这里我强烈建议你手动选择“x64”,因为现在64位应用是主流。如果你确实需要32位库,再选择“Win32”。选好后,点击“Finish”。
CMake会开始第一次配置,分析你的系统环境、编译器,并检查必需的依赖。命令行窗口会刷刷地跑信息。这个过程可能会花一两分钟。如果一切顺利,配置完成后,中间区域的列表会变成红色(CMake GUI的默认行为),这意味着有新的或修改过的配置项可供你调整。
第三步,关键配置项调整。现在你需要关注几个重要的配置项。你可以直接在搜索框输入关键词来快速定位:
BUILD_SHARED_LIBS:这个选项决定编译生成动态库(DLL)还是静态库(LIB)。默认是ON,即生成动态库proj_9_1.dll。如果你希望将PROJ直接链接到你的exe中,不依赖额外的DLL,可以把它设为OFF,这样生成的就是静态库proj.lib。根据你的项目需求决定。ENABLE_CURL和ENABLE_TIFF:这些是可选依赖。PROJ可以通过网络获取最新的网格偏移数据(通过CURL),或者使用TIFF格式的网格文件。如果你不需要这些高级功能,可以将它们设为OFF,简化编译依赖。首次编译建议先关闭,以降低复杂度。- 最重要的:
SQLITE3_INCLUDE_DIR和SQLITE3_LIBRARY。这是成败的关键。CMake第一次配置后,很可能会提示找不到SQLite3。你需要手动指定。- 对于
SQLITE3_INCLUDE_DIR,点击路径选择按钮,定位到你下载的sqlite-amalgamation-*.zip解压后唯一的那个头文件sqlite3.h所在的目录。 - 对于
SQLITE3_LIBRARY,你需要指定sqlite3.lib文件。如果你自己用VS编译了SQLite3,就指向那个.lib文件。如果用的是官网预编译的DLL,你需要先用它生成.lib。简单的方法是:将sqlite3.def文件(在预编译包中)放到DLL同级目录,用VS开发人员命令提示符执行lib /def:sqlite3.def /out:sqlite3.lib /machine:x64来生成。然后在此处选择生成的sqlite3.lib。
- 对于
配置完这些后,再次点击“Configure”按钮。红色区域会刷新,如果还有错误(比如其他依赖没找到),根据错误信息继续调整。直到所有红色错误消失,只剩下白色和灰色的配置项。
第四步,生成与打开。点击“Generate”按钮。CMake会根据你的配置,在构建目录(D:\Dev\proj-9.1.1\vs2022)下生成完整的Visual Studio 2022解决方案文件(PROJ.sln)。看到“Generating done”的日志,就大功告成了。最后,点击“Open Project”按钮,CMake会自动用VS2022打开这个解决方案。
3. Visual Studio 2022中的编译与常见错误解决
用VS2022打开生成的PROJ.sln后,你会看到解决方案资源管理器里有很多项目,比如proj、projinfo、cct等等。我们最核心的目标是proj这个项目,它生成主要的库文件。
在编译之前,先确认两件事:
- 解决方案配置:在VS顶部的工具栏,确保你选择的是你想要的配置,比如“Release”和“x64”。如果你想调试PROJ内部代码,就选“Debug”,但最终发布时用“Release”。
- 生成目标:在解决方案资源管理器中,右键点击
proj项目(或者其他你需要的工具项目如projinfo),选择“生成”。如果只想编译库,就生成proj项目;如果想编译所有内容(包括命令行工具),可以右键点击解决方案名称,选择“重新生成解决方案”。这会花费较长时间,因为要编译PROJ本身及其依赖的第三方库(如libtiff、nlohmann_json等,如果你开启了相关选项)。
现在,点击“生成”。理想情况下,你会在一阵CPU风扇狂转后,在输出窗口看到“全部重新生成: 成功 1 个,失败 0 个”的字样。恭喜你,编译成功了!编译生成的库文件(.lib、.dll)和可执行文件(.exe)会位于你构建目录下的bin和lib子目录中,并且按Debug、Release等配置分文件夹存放。
但是,编译过程很少一帆风顺。下面是我遇到过的几个典型错误及解决办法:
错误1:无法打开包括文件: “sqlite3.h”: No such file or directory这是最经典的错误,说明CMake没有正确找到SQLite3的头文件路径。即使你在CMake GUI里指定了,有时VS工程里的包含目录设置也可能没更新。解决办法:回到CMake GUI,确保SQLITE3_INCLUDE_DIR路径绝对正确(直接指向包含sqlite3.h的文件夹),然后先点“Configure”,再点“Generate”,最后重新用VS打开工程。不要直接在VS里改包含目录,那会被下一次CMake生成覆盖。
错误2:LNK1181: 无法打开输入文件“sqlite3.lib”同样,这是库文件链接错误。解决办法:检查CMake中SQLITE3_LIBRARY的路径,确保指向的.lib文件真实存在且是64位的(如果你编译的是x64)。同样,在CMake中修正后重新Configure和Generate。
错误3:C1083: 无法打开包括文件: “windows.h” 或 其他SDK相关错误这通常是因为VS2022安装的Windows SDK版本问题或者路径混乱。解决办法:打开Visual Studio Installer,点击“修改”你的VS2022,确保安装了正确版本的Windows 10/11 SDK。然后,在VS2022中,可以尝试右键点击项目 -> 属性 -> 常规 -> Windows SDK版本,选择一个已安装的版本。更根本的方法是,在CMake配置时,通过指定-DCMAKE_SYSTEM_VERSION=10.0(具体版本号)参数来强制指定SDK版本,但这通常需要在命令行进行CMake配置。
错误4:编译第三方依赖(如libtiff)时网络超时或下载失败PROJ的CMake脚本会自动下载并编译一些第三方依赖。在国内网络环境下,这很容易失败。解决办法:有两种思路。一是使用科学的上网方式(此处需注意内容安全规范,不能展开)。二是提前下载好这些依赖的源码包。你可以观察CMake配置时的输出日志,看它试图从哪个URL下载什么文件,然后手动下载并放到构建目录下的.cache文件夹(CMake会自动创建)的对应子目录中。虽然麻烦,但一劳永逸。
错误5:生成成功后,运行测试程序崩溃或提示找不到DLL如果编译的是动态库(DLL),那么生成的.exe工具(如projinfo.exe)运行时需要能找到proj_9_1.dll。解决办法:要么将bin\Release目录添加到系统的PATH环境变量,要么将proj_9_1.dll复制到exe的同级目录。对于你自己的项目,也需要确保DLL在可被找到的路径下。
4. 库文件的整理与在项目中的集成使用
编译成功只是第一步,把这些散落在各处的头文件、库文件规整好,方便自己的项目调用,才是最终目的。我习惯创建一个独立的第三方库目录,比如D:\Libraries,在里面为每个库建立单独的文件夹。
对于PROJ 9.1.1,我建议这样组织:
D:\Libraries\proj-9.1.1-vs2022-x64\ ├── include\ # 所有头文件 │ ├── proj.h │ └── ... (其他所有.h文件) ├── lib\ │ ├── Debug\ # Debug版库文件 │ │ ├── proj_d.lib (静态库,如果编译了) │ │ └── proj_9_1_d.lib (导入库,对应动态库) │ └── Release\ # Release版库文件 │ ├── proj.lib │ └── proj_9_1.lib └── bin\ ├── Debug\ # Debug版动态库和工具 │ ├── proj_9_1_d.dll │ ├── proj_9_1_d.pdb (调试符号文件) │ └── projinfo.exe 等工具 └── Release\ # Release版动态库和工具 ├── proj_9_1.dll └── projinfo.exe 等工具头文件:需要从两个地方收集。一是源码目录下的include\proj\文件夹里的所有.h文件,二是源码目录下src\文件夹里的所有.h文件。把它们都复制到你的include目录下。注意保持include\proj\这个子目录结构,因为源码里是#include <proj.h>这样引用的。
库文件:在你CMake构建目录(例如D:\Dev\proj-9.1.1\vs2022)下,找到lib\Release和lib\Debug文件夹,里面的.lib文件就是你要的。如果是动态库,对应的.dll和.pdb文件在bin\Release和bin\Debug下。
整理好后,如何在你的VS项目中集成呢?假设你有一个自己的C++项目:
- 包含目录:在项目属性 -> C/C++ -> 常规 -> 附加包含目录中,添加
D:\Libraries\proj-9.1.1-vs2022-x64\include。 - 库目录:在项目属性 -> 链接器 -> 常规 -> 附加库目录中,添加
D:\Libraries\proj-9.1.1-vs2022-x64\lib\Release(根据你的配置选择Debug或Release)。 - 附加依赖项:在项目属性 -> 链接器 -> 输入 -> 附加依赖项中,添加
proj_9_1.lib(如果你用动态库)或proj.lib(如果你用静态库)。对于Debug配置,则添加proj_9_1_d.lib或proj_d.lib。 - 预处理器定义(如果使用静态库):如果你编译并链接的是静态库,为了避免DLL链接冲突,通常需要在你的项目预处理器定义中添加
PROJ_STATIC。 - 复制DLL(如果使用动态库):将对应配置(Debug/Release)的
proj_9_1.dll(或proj_9_1_d.dll)复制到你的项目可执行文件(.exe)的输出目录下,或者将其所在目录加入系统PATH。
完成这些设置后,你就可以在你的代码中#include <proj.h>,然后愉快地调用PROJ强大的坐标转换函数了。例如,一个简单的从WGS84经纬度转Web墨卡托的代码片段就能跑起来了。自己编译的库,用起来心里特别有底,遇到问题也容易跟踪和排查。整个过程虽然步骤不少,但每一步都有清晰的逻辑,一旦走通,你对整个项目的构建和依赖管理会有更深的理解。
