简介面向需要在Windows下编译64位libtiff和libgeotiff的C开发者尤其适合从事GeoTIFF读写、遥感或GIS图像处理的项目人员。资源为单个doc格式的编译笔记压缩包仅31KB适合快速查阅。作者基于tiff-4.0.6和libgeotiff-1.4.0源码记录在VS2013 x86本机工具命令提示符下通过vcvarsall.bat x64切换到64位编译环境再执行nmake /f makefile.vc生成库文件的全过程。文档还交代了三个关键细节先将VC目录复制到不含空格的D盘根目录以规避命令提示符对路径空格的限制port目录缺少libport.lib时需先单独编译生成libxtiff目录缺少头文件、链接阶段找不到libtiff_i.lib时需要对应拷贝头文件和库文件到指定位置。同时提醒重复编译前删除旧文件。按步骤操作可顺利产出64位的.lib与.dll文件为后续开发GeoTIFF读写程序奠定基础。该资源已有949人学习适合不太熟悉源码编译的开发者作为参考。1. 64位libtiff和libgeotiff编译什么场景下才需要自己动手先说结论如果你只是写代码调API用系统包管理器装好的libtiff和libgeotiff就够了但凡是需要把这两个库嵌进自己的GIS工具链、做离线影像处理、或者目标机器是内网隔离环境编译这一步迟早躲不掉。尤其是64位库很多遗留项目还挂着32位的so或dll一旦数据量超过2GB上限、或者要跟其他64位模块混链问题就暴露出来了。这个标题看着简单实际踩一遍会发现真正的难点不在“敲三条命令”而在依赖顺序、configure缓存、以及64位与32位混用留下的后遗症。本文按我自己惯用的编译流程来写覆盖从工具链准备到最终验证新手可以照着跑熟手可以只看参数表和避坑章节。2. 编译前的准备工作工具链、依赖库和目录规划2.1 先确认工具链是64位的很多人编译失败的第一步不是参数写错而是工具链本身还是32位的。Linux下用gcc -v和uname -m先对齐Windows下如果你用MSYS2环境需要确认用的是mingw64的shell而不是32位的那个入口。uname -m # x86_64 说明系统是64位 gcc -m64 -v 21 | grep Target: # 输出类似 x86_64-linux-gnu确认gcc默认目标架构是64位 file /usr/bin/gcc | cut -d, -f2 # 看gcc可执行文件的二进制格式ELF 64-bit 才是对的这里有个容易被忽略的细节gcc -v不打印目标架构细节grep Target才是关键。如果你看到的是i686-linux-gnu说明gcc默认编译32位代码这时候即使configure参数写对最终产物也是32位的需要在环境变量里加-m64强制指定。Windows上同理用objdump -f /mingw64/bin/gcc.exe看文件头如果显示i386说明你进的shell不对得换mingw64的环境。工具链架构错了后面所有排查都是白费功夫。2.2 依赖顺序libtiff在前libgeotiff在后libgeotiff不是独立存在的它依赖libtiff提供的TIFF读写能力同时还需要PROJ库处理坐标参考系相关的数据结构。所以编译顺序必须是先libtiff再PROJ可选最后libgeotiff。我的建议是PROJ也自己编译一遍不要用系统自带的旧版本。原因有两个一是系统包管理器里的PROJ版本往往偏旧libgeotiff对PROJ的API版本敏感版本不匹配会出现proj.h: No such file or directory这类报错二是内网部署场景下目标机器不一定有对应的运行时包全静态编译才省心。依赖检查可以提前做# 检查系统是否已有libtiff避免混淆 ldconfig -p | grep libtiff # 检查PROJ相关头文件是否存在 find /usr/include -name proj.h 2/dev/null find /usr/local/include -name proj.h 2/dev/null如果系统里同时存在/usr/lib/x86_64-linux-gnu/libtiff.so和/usr/local/lib/libtiff.so后面configure时需要格外小心链接器默认搜索顺序可能导致libgeotiff链接到错误的TIFF库这种问题排查起来非常隐蔽。2.3 目录规划别把所有东西装到系统路径我习惯把第三方库统一装到一个隔离目录比如$HOME/opt/geo64而不是默认的/usr/local。好处是卸载时直接删目录就行多个项目需要不同版本时互不干扰部署到内网机器时可以整个目录打包装过去。export PREFIX$HOME/opt/geo64 export PATH$PREFIX/bin:$PATH export LD_LIBRARY_PATH$PREFIX/lib:$LD_LIBRARY_PATH export PKG_CONFIG_PATH$PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH这四个环境变量是编译这套库的核心。PREFIX决定安装位置PATH保证编译脚本能调到自带的工具LD_LIBRARY_PATH让运行时的动态链接器优先找到自编译的库PKG_CONFIG_PATH让libgeotiff的configure能找到libtiff的.pc文件。建议把这四行写进~/.bashrc或一个独立的setenv.sh每个新终端先source一下。3. 编译64位libtiffconfigure参数与关键选项3.1 最小可用的configure命令libtiff从4.0.x之后默认就支持64位关键在于如何保证构建出来的库是纯64位、并且按你的需求开闭功能。以下是一组适合GIS场景的配置cd tiff-4.5.1 export CFLAGS-O2 -m64 -fPIC export LDFLAGS-m64 ./configure --prefix$PREFIX \ --enable-shared \ --enable-static \ --disable-rpath \ --disable-jpeg12 \ --with-jpeg-include-dir$PREFIX/include \ --with-jpeg-lib-dir$PREFIX/lib \ --with-zlib-include-dir$PREFIX/include \ --with-zlib-lib-dir$PREFIX/lib make -j$(nproc) make install这里-m64是硬性指定64位代码生成-fPIC必须加不然后面libgeotiff生成动态库时链接会报relocation R_X86_64_32 against .rodata之类的错误。--disable-jpeg12是关掉12位JPEG支持这个功能很少用且会和标准JPEG库产生符号冲突关了省心。--disable-rpath可能有人觉得奇怪它防止编译时把绝对路径写死在库文件里。如果你后续要迁移到别的机器rpath会导致动态链接器优先去找编译机路径下不存在的库反而制造麻烦。不加这个参数的后果在目标环境路径不同时才会暴露。3.2 必调的jpeg和zlib支持libtiff对JPEG和ZIP压缩的支持是运行时质量的关键。GIS影像产品里GeoTIFF压缩常用的就是JPEG、LZW和Deflate。如果configure时没有检测到zlibTIFF文件写入时CRC校验会出问题没有jpeg则打不开大部分航拍影像和扫描地形图。上面命令里--with-jpeg-include-dir和--with-zlib-include-dir指向了$PREFIX这要求你提前编译好jpeg和zlib。常见做法是先用同样的CFLAGS-m64 -fPIC编译这两个基础库再装到$PREFIX。configure输出里有一段关键信息需要人工确认checking for libjpeg... yes checking for zlib... yes checking for liblzma... yes checking for libwebp... no前三项必须为yes最后一项webp看需求。如果显示no多半是头文件路径没找到回到3-1小节的--with-*参数检查。注意这里的--with-*参数在不同libtiff版本里命名不完全一致4.3.x用--with-jpeg-include-dir更早的版本直接传CPPFLAGS和LDFLAGS就行先跑./configure --help | grep with-jpeg确认。3.3 验证libtiff编译结果的位宽编译完成后别急着装libgeotiff先花一分钟验证一下产物。file $PREFIX/lib/libtiff.so # 期望输出: ELF 64-bit LSB shared object, x86-64 $PREFIX/bin/tiffinfo --version # 能正常打印版本号说明动态库依赖解析成功 ldd $PREFIX/lib/libtiff.so | grep not found # 没有输出说明所有依赖都找到了这里file是最直接的位宽验证手段比看configure日志可靠得多。有些坑是你明明记得编译时加了-m64但某个子模块的Makefile覆盖了CFLAGS导致最终产物是32位只有file能一锤定音。还有一个细节如果重新用不同参数编译过libtiff必须make clean之后再configure。libtiff的Makefile会把第一次configure的配置缓存下来不清理直接换参数很可能出现改了个寂寞的情况。当时的场景某项目在同一份源码目录里先编了32位版做验证再编64位版时忘了make distclean产物反复检查都是32位最后发现是陈旧目标文件在捣乱。4. 编译64位libgeotiff链接libtiff和PROJ的正确姿势4.1 先确认PROJ库的完整状态libgeotiff本身只管地理坐标到TIFF标签的映射真正的坐标转换依赖PROJ。所以libgeotiff的编译若想完整支持基准面转换必须先有PROJ库的原件。ls $PREFIX/lib/libproj.* # libproj.a 和 libproj.so 都在才算完整 $PREFIX/bin/projinfo --version # 能调用说明proj可执行文件正常 head -20 $PREFIX/lib/pkgconfig/proj.pc # 检查.pc文件里 prefix 路径对不对PROJ版本我一般选7.x或9.x这两个大版本接口相对稳定。libgeotiff 1.7.x对应PROJ 6以上的API1.6.0以下则可能需要PROJ 5的老API。版本匹配不上时configure会报cannot find PROJ实际上PROJ是装了的只是API太新或太旧对不上号。如果目标机器上完全没有PROJ需求比如只做不涉及坐标系转换的纯TIFF读写可以用--without-proj关掉。但实际地理数据处理极少有不涉及坐标关系的建议保留。4.2 编译命令与参数说明cd libgeotiff-1.7.1 export CPPFLAGS-I$PREFIX/include export LDFLAGS-L$PREFIX/lib -m64 ./configure --prefix$PREFIX \ --with-libtiff$PREFIX \ --with-proj$PREFIX \ --with-jpeg$PREFIX \ --enable-shared \ --enable-static make -j$(nproc) make install--with-libtiff和--with-proj是libgeotiff最核心的两个配置项指向前面装好的目录。configure脚本会去这些目录下找头文件和库文件如果找不到就直接报错退出不会跳过这对排错来说是好事。有个容易踩的坑libgeotiff的configure优先找静态库还是动态库取决于环境里的--enable-static参数。如果你同时需要静态和动态上面两个参数都写上。但注意如果libtiff本身只编了动态库libgeotiff编译时可能提示找不到libtiff.a这时检查libtiff的configure配置必须开--enable-static。4.3 让libgeotiff找到libtiff的运行时依赖编译成功后还要处理运行时的库搜索路径问题。libgeotiff的动态库依赖libtiff的动态库当你把libgeotiff拷贝到别的机器时需要同时带上libtiff和PROJ的so文件。可以用ldd检查依赖关系ldd $PREFIX/lib/libgeotiff.so # 期望看到 libtiff.so - $PREFIX/lib/libtiff.so # 期望看到 libproj.so - $PREFIX/lib/libproj.so # 如果显示 not found说明运行时路径没配置好如果src输出显示libtiff.so /usr/lib/x86_64-linux-gnu/libtiff.so说明libgeotiff链接到了系统库而不是自编译的64位库。原因通常是编译时LD_LIBRARY_PATH没设置或者libtiff的.so文件里没有足够的信息引导链接器。解决方法是回到4.2小节在configure前重新exportLD_LIBRARY_PATH$PREFIX/lib然后make clean make。链接时代的-L路径只影响编译时查找运行时是另一套机制。另一种更彻底的做法是编译时直接给libgeotiff的Makefile传-Wl,-rpath,$PREFIX/lib./configure ... LDFLAGS-L$PREFIX/lib -Wl,-rpath,$PREFIX/lib这样生成的so文件里直接嵌入了运行时搜索路径无论LD_LIBRARY_PATH怎么变都能找到。代价是路径写死了移动目录之后会失效所以这个做法只适用于固定部署位置的场景。5. 64位编译常见问题排查链接器玄学与版本残留5.1 32位库残留导致链接错乱现象libgeotiff的configure报了undefined reference to TIFFOpen但这个符号明明在libtiff里是存在的。原因系统里存在两个libtiff版本一个是32位的老版本一个是新装的64位版本。链接器先找到了32位的.so目标文件架构不匹配导致后续所有符号都无法解析。解决先用第五章之前的ldd确认实际链接了哪个库。然后强制指定路径export LIBTIFF_CFLAGS-I$PREFIX/include export LIBTIFF_LIBS-L$PREFIX/lib -ltiff ./configure --with-libtiff$PREFIX关键是把-L$PREFIX/lib放在链接器搜索路径的最前面。LIBTIFF_CFLAGS和LIBTIFF_LIBS是libgeotiff configure脚本支持的覆盖变量直接注入就不会被系统路径干扰。5.2 configure缓存导致的“假成功”现象configure输出显示找到了libtiff和PROJ但make的时候报头文件找不到或者make install后调用程序时加载库失败。原因configure脚本会把config.status和config.cache写进源码目录修改了环境变量后直接重跑configure部分检查结果可能还是缓存的旧值。解决重新configure之前执行清理make distclean rm -f config.cache config.status ./configure ...这两个文件残留是纯玄学问题的第一大来源。有些开发者看到configure日志变了就以为配置生效了实际上源码编译路径里还引用着旧的Makefile片段。5.3 动态库搜索顺序没有覆盖自编译路径现象程序运行时报错error while loading shared libraries: libgeotiff.so.5: cannot open shared object file原因动态链接器的搜索顺序是RPATH、LD_LIBRARY_PATH、/etc/ld.so.conf、默认系统目录。你的程序没有设置RPATHLD_LIBRARY_PATH又没包含PREFIX目录。解决在启动脚本里加环境变量或者用ldconfig注册export LD_LIBRARY_PATH$PREFIX/lib:$LD_LIBRARY_PATH # 或者写入系统配置 echo $PREFIX/lib /etc/ld.so.conf.d/geo64.conf ldconfig内网部署时我更倾向于用前者因为ldconfig会污染全局环境多套环境共存时容易互踩。5.4 多版本PROJ的API钩子冲突现象libgeotiff编译通过但运行时调用GTIFProj4ToLatLong等坐标转换函数时崩溃或返回空结果。原因libgeotiff链接的PROJ和系统其他组件加载的PROJ版本不一致两个版本的pj_context结构体大小或成员顺序不同传参后内存越界。解决先用ldd libgeotiff.so确认它实际链接的是哪个PROJ。如果系统里存在多个版本建议给libgeotiff编译时传-Wl,-rpath强制锁定到自编译的PROJ目录不要让动态链接器自由发挥。5.5 头文件与库文件版本不匹配现象调用GTIFNew编译时报incomplete type错误或者某个结构体字段访问不了。原因编译时用的头文件来自新版libgeotiff但链接的.so来自旧版或者反过来。源码树和安装树混了。解决严格按第4章的安装路径走头文件和库文件都从$PREFIX下取。确认编译命令里-I和-L指向同一个前缀不要前者指向源码树后者指向系统路径。6. 用实测验证编译产物从依赖链到坐标读写闭环编译安装全部完成之后我会用一个小测试程序做端到端验证而不是只看“能编译”就收工。这个习惯避免过很多次部署现场翻车。6.1 验证依赖链完整性ldd $PREFIX/lib/libgeotiff.so # 预期输出中不存在 not found 条目 readelf -d $PREFIX/lib/libgeotiff.so | grep NEEDED # NEEDED条目里应包含 libtiff.so 和 libproj.soreadelf的输出比ldd更底层它直接读ELF文件的动态段不会因为环境变量而给出“假正常”的结果。如果上面两条命令的输出不一致说明当前环境的LD_LIBRARY_PATH和文件本身的依赖需求有差异。6.2 写一个最小坐标写入程序用C写个十几行的小程序创建一个带地理参考的GeoTIFF写入经纬度坐标信息然后回读校验。#include geotiffio.h #include xtiffio.h #include stdio.h int main(void) { const char* path /tmp/test_geo64.tif; GTIF* gt NULL; TIFF* tif NULL; tif XTIFFOpen(path, w); if (!tif) return 1; gt GTIFNew(tif); if (!gt) { XTIFFClose(tif); return 1; } double lon 116.0; double lat 39.0; GTIFKeySet(gt, GeogGeodeticDatumGeoKey, TYPE_SHORT, 1, 4326); GTIFKeySet(gt, GeogLinearUnitsGeoKey, TYPE_SHORT, 1, 9001); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, 64); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, 64); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); unsigned char buf[64] {0}; for (int row 0; row 64; row) { TIFFWriteScanline(tif, buf, row, 0); } GTIFWriteKeys(gt); GTIFFree(gt); XTIFFClose(tif); printf(write done\n); return 0; }编译时链接参数如下gcc -m64 test_geo.c -o test_geo \ -I$PREFIX/include \ -L$PREFIX/lib \ -lgeotiff -ltiff -lproj运行后再用libgeotiff自带的listgeo工具检查生成的GeoTIFF是否带正确的投影信息$PREFIX/bin/listgeo /tmp/test_geo64.tif # 输出中包含 Geotiff_Information 标签且无报错即通过这个测试程序验证的核心点TIFFTag整体写入干净、GTIFKey和TIFFTag能协同保存、64位模式下指针和文件偏移正常。如果在32位上能跑、64位上崩溃基本可以断定是自编译的链接选项问题回到第5章的检查项。6.3 关于静态库与部署策略的收尾最后聊一下静态库和动态库的选择。GIS工程项目发到内网现场时我倾向于把libtiff、libgeotiff、PROJ全部编译成静态库然后应用本身打成静态链接的可执行文件。原因很朴素目标机器上没人知道LD_LIBRARY_PATH是什么也不一定允许改系统环境变量。但静态链接也有代价可执行文件体积直接增加数MB补丁更新要重新编译整个应用。如果你维护的是常态化迭代的软件产品而不是一次性交付的现场工具动态库方案加rpath是更省心路径。这个取舍没有绝对正确答案我的习惯是看现场机器的操作系统版本分布系统老且混乱的用静态版本统一可控的用动态。编译方法只能是解题的手段之一最终的交付形态才是工程项目里真正需要思量的部分。希望这篇笔记能帮你在libtiff和libgeotiff的64位编译上少走几个来回。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?