首页 / 资讯中心 / 文章详情

Windows下OSG+osgEarth预编译3rdParty库CMake避坑

Windows下OSG+osgEarth预编译3rdParty库CMake避坑 ★ FEATURED ARTICLE
简介面向在 Windows 平台使用 Visual Studio 2019 编译 OSG3.6.5 与 OSGEarth2.10 的 C 开发者这份预编译 64 位三方库包用于补齐编译链路中的依赖项减少从源码逐个构建第三方组件的重复工作。压缩包共约 2000 个文件大小 137.76MB包含大量 h 头文件、lib 静态/导入库、dll 动态库以及 cmake 配置、inc、c/cpp、hpp、inl、proto 等源码与构建相关文件并附带 html 文档和时区数据等辅助内容便于在工程中配置包含目录、库目录与链接项。已有 626 人学习下载适合需要快速搭建 OSG/OSGEarth 开发环境、排查依赖缺失与链接错误的中高级图形开发者参考使用。1. 为什么 Windows 上搭 OSG osgEarth总是死在第三方库这一步做过三维地球、或者基于 OSG 做渲染的大概都试过在 Windows 上从源码编一次 OSG3.6.5 加 osgEarth。编 OSG 本身还算温和真正让人卡住的是它脚下那堆三方库GDAL、CURL、PROJ、libzip、SQLite3各自带着一套 CMake 选项和版本约束。自己逐个编快则一天慢则一周还未必能把 ABI 对齐。这份预编译好的 3rdParty.zip 干的就是这件事——按 VS2019v142工具集编好include、lib、bin 一把打包。往下讲它怎么接进 CMake、参数怎么设、哪里最容易翻车适合已经装好 VS2019、想跳过依赖环节直接写业务代码的人。2. 3rdParty 包的目录与 ABI先看清 v142 这三个字意味着什么拿到压缩包先别急着解压到 C 盘根目录。很多人第一步就踩坑解到一个带空格、带中文的路径下比如D:\我的项目\osg 依赖\3rdParty然后 CMake 里各种NOTFOUND回头查半天以为是库不全其实是路径里的空格把find_library的参数截断了。我一般解到D:\osg\3rdParty这种短、纯英文、无空格的路径后面所有命令都基于它。2.1 include / lib / bin 三段式布局与真正常被忽略的 shareOSG 系预编译三方库基本都遵循同一套布局解压后第一层目录应该是这样D:\osg\3rdParty\ ├── include\ # 所有依赖的头文件平铺在一起gdal.h、curl\curl.h、proj.h… ├── lib\ # 导入库 .libDebug 版有的带 d 后缀 ├── bin\ # 运行时 DLL外加 osgdb_gdal.dll 这类插件依赖 └── share\ # GDAL_DATA、PROJ_DATA 之类的数据目录容易被当成垃圾删掉include和lib是编译期用的bin是运行期用的这两层的分工大家都清楚。真正容易被忽略的是share——GDAL 做坐标转换要读gdalvrt.xsd、data\下的 EPSG 字典PROJ 要读proj.db。这两个目录没有出现在include/lib/bin三段式里很多人第一次清理磁盘时顺手删了结果程序能编能跑就是坐标偏出几百米或者osgEarth加载影像时投影报错。我一般会在解压后先做一次校验# 在 Git Bash 里跑确认三类关键文件都在 ls D:/osg/3rdParty/lib | grep -iE gdal|proj|curl|sqlite3|zip | head -20 ls D:/osg/3rdParty/bin | grep -iE gdal.*\.dll|proj.*\.dll | head -20 ls D:/osg/3rdParty/share 2/dev/null || echo share 目录缺失PROJ/GDAL 数据需要单独补第一行确认导入库存在第三行确认share是否随包附带。有些精简版 3rdParty 会把share去掉以减小体积这种情况下必须自己从对应上游版本把proj.db和 GDAL 的data目录拷进来否则后面坐标转换会静默出错——它不崩只是结果不对这种问题最难查。2.2 为什么 mingw 或 v143 编出来的库不能直接塞进来MSVC 的 ABI 不是铁板一块。VS2015 之后微软承诺了 v140 到 v143 的二进制兼容但这条承诺有几个前提C 运行时库版本要能对上、_ITERATOR_DEBUG_LEVEL要一致、RuntimeLibrary/MD 还是 /MT要一致。预编译包最大的价值就在于它替你把这一串约束锁死了。具体到 VS2019工具集是 v142_MSC_VER是 192916.11 是 192916.0 是 1920。如果你拿一份 MinGW-w64 编的 GDAL 导入库往项目里塞链接器会直接报符号找不到因为 MinGW 的 name mangling 和 MSVC 完全是两套体系__imp_gdal_open和_gdal_open8根本对不上。更隐蔽的是用 VS2022 的 v143 编的库链接能过运行时偶尔崩因为 v143 的 STL 内部实现有几处布局微调跨版本传std::string、std::vector这种带堆内存的对象时一侧的_Container_base12和另一侧对不上析构时就是一次野指针访问。所以这份包的关键不是“有 GDAL”而是“GDAL 是用 v142 编的”。判断方法很简单用 dumpbin 看导入库或者 DLL 的运行时依赖# 用 VS2019 自带的 dumpbin路径以本机实际安装为准 C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\dumpbin.exe ^ /dependents D:\osg\3rdParty\bin\gdal*.dll输出里如果出现VCRUNTIME140.dll、MSVCP140.dll说明是 MSVC 运行时体系如果出现libgcc_s_seh-1.dll、libstdc-6.dll那就是 MinGW 编的直接不能用。这一步花三十秒能省掉后面半天的链接报错排查。2.3 版本对齐表OSG 3.6.5 吃哪些库、osgEarth 又多吃哪些OSG 3.6.5 本体对三方库的依赖其实很克制主要是图像编解码和网络加载osgEarth 才是真正的依赖大户它把 GDAL 当几何与栅格引擎、把 PROJ 当坐标转换引擎、把 CURL 当瓦片下载通道。两者对同一份 3rdParty 的消费方式不一样依赖OSG 3.6.5 是否直接使用osgEarth 是否直接使用缺失后的表现zlib是png/tiff 的底层间接编译期找不到zlib.hlibpng / libjpeg / libtiff是osgdb_png 等插件间接插件编不出来读图返回空freetype是osgText是文字渲染段错误curl是osgdb_curl 插件是网络瓦片远程瓦片 404 或超时GDAL否是.earth配置里 GDAL 驱动全部失效PROJ否是坐标转换结果偏移且不报错SQLite3否是feature 缓存缓存无法写入日志里刷 warninglibzip否是kmz 读取.kmz打开失败这张表的意义在于定位当链接报unresolved external symbol时先看符号属于哪一列就知道该去查 OSG 的 Find 脚本还是 osgEarth 的。OSG 侧找不到库通常是ACTUAL_3RDPARTY_DIR没生效osgEarth 侧找不到则是CMAKE_PREFIX_PATH或者依赖的传递版本不匹配——尤其是 GDAL 3.x 要求 PROJ 6 以上版本一旦错配GDAL 自己编得出来但调proj_normalize_for_visualization时会返回空。提示不要试图用 vcpkg 再装一份 GDAL 去补这个包缺的部分。两套 GDAL 混在一个链接单元里符号重复定义只是表象真正的问题是两份proj.db版本不同、坐标轴的轴序约定不同排查成本远高于重新找一份完整的 3rdParty。3. 接进 CMakeOSG 3.6.5 从配置到 install 的完整链路库齐了下一步是让 OSG 的 CMake 认出它们。OSG 的构建脚本写得比较“老派”它不依赖find_package的现代模式而是自己在CMakeModules\Find3rdPartyDependencies.cmake里逐个find_pathfind_library搜索根目录来自一个叫ACTUAL_3RDPARTY_DIR的变量。理解这一点后面所有奇怪行为都能解释。3.1 两个路径变量ACTUAL_3RDPARTY_DIR 与 CMAKE_PREFIX_PATHACTUAL_3RDPARTY_DIR是 OSG 自己的私有变量只对 OSG 的 CMake 生效。它的作用是告诉Find3rdPartyDependencies.cmake去这个目录下的include找头文件、lib找.lib、bin找 DLL。设了它OSG 会拼出类似D:/osg/3rdParty/lib/osg160-osg.lib的完整路径去探测。CMAKE_PREFIX_PATH是 CMake 的通用机制走的是prefix/include、prefix/lib、prefix/bin这套约定。它对 osgEarth 更关键因为 osgEarth 用的是标准find_package(GDAL)、find_package(CURL)这些脚本认CMAKE_PREFIX_PATH而不认 OSG 的私有变量。稳妥的做法是两个都设指向同一份 3rdParty# 这两个变量最好同时存在覆盖 OSG 私有查找和标准 find_package 两条路径 set(ACTUAL_3RDPARTY_DIR D:/osg/3rdParty CACHE PATH OSG 3rdParty 根目录) set(CMAKE_PREFIX_PATH D:/osg/3rdParty CACHE PATH 标准 find_package 搜索根)参数说明CACHE PATH表示这个值会写进CMakeCache.txt后续重新配置沿用缓存如果换了 3rdParty 路径必须把CMakeCache.txt删掉重新生成否则旧路径会被缓存锁死改命令行参数也不生效——这是新手最常见的“我明明改了啊”的来源。3.2 一条能直接复制的生成命令假设 OSG 源码在D:\osg\OpenSceneGraph-3.6.5安装目标在D:\osg\install在源码目录下开一个 cmd 窗口cmake -S . -B build-vs2019 -G Visual Studio 16 2019 -A x64 ^ -DACTUAL_3RDPARTY_DIRD:/osg/3rdParty ^ -DCMAKE_PREFIX_PATHD:/osg/3rdParty ^ -DCMAKE_INSTALL_PREFIXD:/osg/install ^ -DBUILD_OSG_APPLICATIONSON ^ -DBUILD_DOCUMENTATIONOFF ^ -DCMAKE_CONFIGURATION_TYPESDebug;Release逐项说清楚-G Visual Studio 16 2019 -A x64指定生成器和目标架构架构必须和 3rdParty 的位数一致包是 x64 的就必须-A x64混用 32 位会得到一堆LNK1112: 模块计算机类型冲突BUILD_OSG_APPLICATIONSON决定要不要连带编osgviewer、osgconv这些示例程序对验证很有用建议开着BUILD_DOCUMENTATIONOFF关掉文档生成纯粹省时间CMAKE_CONFIGURATION_TYPES限定为 Debug 和 Release 两套避免出现 MinSizeRel 之类没人维护的配置。生成完成后不要急着打开 sln 手动点编译用命令行一次跑完# 先编 Release 并安装 cmake --build build-vs2019 --config Release --target INSTALL -- /m # 再编 Debug 并安装到同一前缀下的 debug 子目录后面会解释为什么 cmake --build build-vs2019 --config Debug --target INSTALL -- /m--target INSTALL是 CMake 的伪目标等价于在 VS 里右键 INSTALL 工程生成-- /m把后面的参数透传给 MSBuild/m表示多核并行编译能把 OSG 的编译时间从四十分钟压到十分钟以内——但第一次编建议先不加/m让报错信息顺序清晰确认没问题后再开并行。3.3 编译产物长什么样、Debug 和 Release 必须分开装编完之后去D:\osg\install看目录结构是这样的D:\osg\install\ ├── bin\ # osg160-osg.dll、osg160-osgDB.dll、osgviewer.exe 等 ├── include\osg\ # 头文件 ├── lib\ # osg160-osg.libRelease 导入库 └── lib\osgPlugins-3.6.5\ # 插件 DLLosgdb_png.dll、osgdb_gdal.dll、osgdb_curl.dll…osgPlugins-3.6.5这个目录名里的版本号是硬编码在 OSG 的 CMake 里的程序运行时会在bin的同级或者OSG_LIBRARY_PATH指定的位置找它。如果把 DLL 从bin拷到别处、却不带上这个插件目录结果就是readNodeFile对.png、.tif一律返回空指针日志里只有一句Warning: Could not find plugin to read objects from file。Debug 和 Release 必须分开装原因是 OSG 的导入库命名Release 是osg160-osg.libDebug 在默认配置下会带d后缀变成osg160-osgd.lib但如果你在同一个build目录里先后生成两种配置install 到同一个前缀会互相覆盖——最后lib目录里只剩最后编的那个配置另一种配置的项目引用时找不到对应.lib。我的做法是生成两个 build 目录# Debug 单独一个 build 目录装到独立前缀 cmake -S . -B build-vs2019-debug -G Visual Studio 16 2019 -A x64 ^ -DACTUAL_3RDPARTY_DIRD:/osg/3rdParty ^ -DCMAKE_PREFIX_PATHD:/osg/3rdParty ^ -DCMAKE_INSTALL_PREFIXD:/osg/install-debug ^ -DCMAKE_DEBUG_POSTFIXd cmake --build build-vs2019-debug --config Debug --target INSTALL -- /mCMAKE_DEBUG_POSTFIXd让 Debug 的产物统一带d后缀这样即使不小心装到同一目录DLL 名也不会冲突。代价是项目里引用时要注意Debug 配置链接osg160-osgd.libRelease 链接osg160-osg.lib用 CMake 的debug/optimized关键字区分target_link_libraries(myapp PRIVATE debug D:/osg/install-debug/lib/osg160-osgd.lib optimized D:/osg/install/lib/osg160-osg.lib)4. 编 osgEarth依赖匹配、选项开关与插件目录OSG 编完只是走了一半。osgEarth 在它之上且对 GDAL、PROJ 的版本比 OSG 苛刻得多。真正让 osgEarth 编译失败的原因九成不是 OSG 没编好而是 GDAL 的版本和 PROJ 的版本对不上或者 CURL 的静态库/动态库选错了。4.1 osgEarth 的 CMake 选项里哪几个真该动osgEarth 的CMakeLists.txt会吐出一大串OSGEARTH_*选项但真正需要手动干预的只有四五个。其余保持默认即可乱改反而更麻烦选项默认建议值为什么BUILD_OSGEARTH_EXAMPLESONON生成osgearth_viewer是后面自检的工具OSGEARTH_USE_QT视环境OFF没装 Qt 时开着会卡在find_package(Qt5)报错BUILD_SHARED_LIBSONON静态链接会把 GDAL 的依赖链全部拖进来OSGEARTH_ENABLE_PROFILINGOFFOFF开着会在每帧插统计代码调试没问题出包记得关CMAKE_INSTALL_PREFIX空与 OSG 同前缀头文件能一套include找齐省得配两遍路径OSGEARTH_USE_QT值得单独说很多机器上装了 Qt 但只装了 MinGW 版本osgEarth 的find_package(Qt5)会找到它然后链接时因为 Qt 是 MinGW 编的、而主体是 MSVC报一堆LNK2019。与其去修不如直接-DOSGEARTH_USE_QTOFF等真正需要 Qt 界面时再针对性地配一套 MSVC 版 Qt。4.2 GDAL / PROJ / CURL / SQLite 的对接口子osgEarth 的FindGDAL.cmake走的是标准流程先找gdal.h再找gdal.lib最后尝试用gdal_version.h读版本号。这条链在 3rdParty 布局正确时一次就过出问题基本集中在两个地方。第一是 GDAL 的库文件名。预编译包里的导入库常见形如gdal.lib但也有的包带版本号gdal305.lib。osgEarth 的查找脚本只认gdal这个名字遇到带版本号的就找不到。解决办法是在 CMake 命令行里显式指定cmake -S . -B build-osgearth -G Visual Studio 16 2019 -A x64 ^ -DCMAKE_PREFIX_PATHD:/osg/install;D:/osg/3rdParty ^ -DGDAL_INCLUDE_DIRD:/osg/3rdParty/include ^ -DGDAL_LIBRARYD:/osg/3rdParty/lib/gdal.lib ^ -DPROJ_INCLUDE_DIRD:/osg/3rdParty/include ^ -DPROJ_LIBRARYD:/osg/3rdParty/lib/proj.lib ^ -DCURL_INCLUDE_DIRD:/osg/3rdParty/include ^ -DCURL_LIBRARYD:/osg/3rdParty/lib/libcurl.lib ^ -DOSGEARTH_USE_QTOFF ^ -DCMAKE_INSTALL_PREFIXD:/osg/install注意CMAKE_PREFIX_PATH这里用了分号分隔两个路径D:/osg/install是刚编好的 OSGD:/osg/3rdParty是三方库。Windows 下 CMake 的列表分隔符就是分号写成逗号或者空格都会失效。第二是 CURL 的静态/动态之争。3rdParty 的lib目录里可能同时有libcurl.lib静态和libcurl_imp.lib动态导入前者要求链接时再补一堆ws2_32、crypt32、wldap32的系统库后者不需要。指向libcurl_imp.lib更省事如果只有静态库就得在target_link_libraries里补系统库否则报一堆unresolved external symbol __imp_WSAStartup。CURL 还有一个常被忽略的点CURL_STATICLIB宏。静态链接 libcurl 时头文件里必须定义这个宏否则curl.h会按动态导入的方式声明函数链接时符号对不上。osgEarth 的 CMake 不会自动加需要手动add_definitions(-DCURL_STATICLIB)PROJ 的坑更隐蔽GDAL 3.x 内部依赖 PROJ如果 3rdParty 里 GDAL 是 3.x、PROJ 却是 5.xGDAL 自己编译时不会报错但运行到坐标转换就会返回空指针。判断方法是看share目录下的proj.db——PROJ 6 之后坐标系统定义全部搬进了这个 SQLite 文件PROJ 5 及以前是没有的。有proj.db就说明是 6 以上和 GDAL 3.x 匹配。4.3 插件目录与 OSG_LIBRARY_PATH 的真实优先级osgEarth 编译完之后bin里会多出osg160-osgEarth.dll、osg160-osgEarthAnnotation.dll、osg160-osgEarthSymbology.dll这一批插件目录osgPlugins-3.6.5里会多出osgdb_earth.dll、osgdb_kml.dll如果 GDAL/OGR 编进来了。运行时OSG 查找插件的顺序是这样的先看环境变量OSG_LIBRARY_PATH里的路径再看可执行文件所在目录再看编译时硬编码的OSG_PLUGINS路径。这个顺序意味着——如果你把 exe 拷到别处测试而OSG_LIBRARY_PATH还指向原来那台机器的安装目录程序会加载旧插件表现出来就是“明明新编了 GDAL 驱动行为还是老的”。调试期我一般这么设# 只在当前 cmd 会话生效避免污染系统环境变量 set OSG_LIBRARY_PATHD:\osg\install\lib\osgPlugins-3.6.5 set PATHD:\osg\install\bin;D:\osg\3rdParty\bin;%PATH% set GDAL_DATAD:\osg\3rdParty\share\gdal set PROJ_DATAD:\osg\3rdParty\share\projPROJ_DATA在旧版本里叫PROJ_LIBPROJ 8 之后改成PROJ_DATA两个都设上不冲突老代码新代码都能跑。GDAL_DATA指向的目录里应该有gdalvrt.xsd、pcs.csv、gcs.csv这几类文件缺了的话GDALAllRegister之后读栅格会报Unable to open EPSG support file。5. 避坑与排查五条踩出来的记录下面这几条都是我在不同机器上真实撞过的按“现象 → 原因 → 解决”写遇到类似的直接对号入座。5.1 链接期LNK2038 与 RuntimeLibrary 值不匹配现象链接 osgEarth 时报LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MT_StaticRelease”不匹配值“MD_DynamicRelease”或者反过来。原因3rdParty 里的某个库是用/MT静态 CRT编的而 OSG 或 osgEarth 的 CMake 默认用/MD动态 CRT。这两套不能混混了就是两套堆管理器跨模块new/delete立刻出问题。解决先确认 3rdParty 用的是哪套。用 dumpbin 看 DLL 依赖有VCRUNTIME140.dll说明是/MD没有则是/MT。确认后在自己的 CMake 里对齐# 和 3rdParty 统一为动态 CRT如果包是 /MT 就改成 MultiThreaded set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$$CONFIG:Debug:DebugDLL)这行必须在project()之前设否则不生效。改完记得删CMakeCache.txt重新生成缓存会锁住旧的运行时设置。5.2 运行期双击闪退缺的是什么 dll现象osgearth_viewer.exe双击后窗口一闪就没了命令行里跑也没有任何输出。原因缺 DLL。Windows 加载器在找不到依赖时会直接终止进程连main都进不去所以不会有任何日志。最常见的是gdal30x.dll、proj_8_2.dll、libcurl.dll这几个——它们既不在bin里也没进PATH。解决用 dumpbin 递归查依赖或者用 Dependencies 工具看。我习惯用命令行快速定位# 递归列出所有找不到的依赖路径换成实际 exe C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\dumpbin.exe ^ /dependents D:\osg\install\bin\osgearth_viewer.exe | findstr /i .dll把列出的一级依赖对照D:\osg\install\bin和D:\osg\3rdParty\bin逐个核缺哪个把所在目录加进PATH再跑。确认没问题后再把这些 DLL 拷到 exe 同级目录做成便携形式。5.3 加载期.earth 文件报插件缺失现象osgearth_viewer test.earth报Warning: Could not find plugin to read objects from file test.earth但文件路径明明是对的。原因osgdb_earth.dll插件没被加载。可能有两种一是插件目录里根本没有这个 DLLosgEarth 编译时没生成二是OSG_LIBRARY_PATH指向了别的地方OSG 去那里找不到。解决先dir看插件目录里有没有osgdb_earth.dll。有的话检查环境变量# 确认 OSG 实际在哪个路径下找插件 echo %OSG_LIBRARY_PATH% # 如果输出是空或者别的路径直接在当前会话覆盖 set OSG_LIBRARY_PATHD:\osg\install\lib\osgPlugins-3.6.5还有一种情况插件 DLL 存在但因为它的依赖比如gdal30x.dll没找到加载时LoadLibrary返回失败OSG 只报“找不到插件”不报“依赖缺失”——这就是典型的黑匣子场景。用 dumpbin 查这个插件 DLL 的依赖比盯着 osgEarth 的日志有用得多。5.4 结果层坐标偏了、地形发白现象影像和矢量叠在一起位置对不上偏了几百米或者地形加载出来一片白没有纹理。原因偏坐标是PROJ_DATA没设程序找不到proj.db坐标转换退化成恒等变换——不报错只是算错。地形发白通常是GDAL_DATA缺失导致 GDAL 无法识别影像的坐标系或者osgdb_gdal.dll插件没加载。解决两个环境变量都补上然后写个小程序验证坐标转换// crs_check.cpp —— 验证 PROJ 数据可用 #include proj.h #include cstdio int main() { // 4326(EPSG:4326) 转 3857(EPSG:3857)源点取经度 116、纬度 39 PJ_CONTEXT* ctx proj_context_create(); PJ* trans proj_create_crs_to_crs(ctx, EPSG:4326, EPSG:3857, nullptr); if (!trans) { std::printf(proj_create_crs_to_crs 失败检查 PROJ_DATA\n); return 1; } PJ_COORD in proj_coord(116.0, 39.0, 0, 0); PJ_COORD out proj_trans(trans, PJ_FWD, in); // 正常结果约 x≈12913060, y≈4711000若等于输入说明转换退化了 std::printf(x%.2f y%.2f\n, out.xy.x, out.xy.y); proj_destroy(trans); proj_context_destroy(ctx); return 0; }判定标准很直接输出接近12913060和4711000就是对的如果输出还是116、39说明 PROJ 找不到数据退化成了恒等变换。这一步能帮你把“坐标偏了”从玄学变成可测的数值。6. 部署与自检让 exe 在没有 VS 的机器上跑起来开发机上能跑不算完真正交付时对方机器上既没装 VS 也没配PATH所有依赖都得跟着 exe 走。这一节讲两件事怎么把 DLL 收齐以及怎么在不启动界面的情况下验证依赖链完整。6.1 用 CMake 的 GET_RUNTIME_DEPENDENCIES 收 DLL手动一个个拷 DLL 是最容易漏的方式因为依赖是传递的osgearth_viewer.exe依赖osg160-osgEarth.dll后者又依赖gdal30x.dll再往下还有proj_8_2.dll、sqlite3.dll。CMake 3.16 之后提供了file(GET_RUNTIME_DEPENDENCIES)能一次性把整条依赖链扒出来。写一个独立的deploy.cmake用cmake -P调用不参与主构建# deploy.cmake —— 收集可执行文件的运行时依赖到统一目录 set(_exe D:/osg/install/bin/osgearth_viewer.exe) set(_search D:/osg/install/bin D:/osg/3rdParty/bin) # 依赖搜索目录 set(_out D:/osg/deploy) # 输出目录 file(MAKE_DIRECTORY ${_out}) file(GET_RUNTIME_DEPENDENCIES EXECUTABLES ${_exe} DIRECTORIES ${_search} RESOLVED_DEPENDENCIES_VAR _resolved PRE_EXCLUDE_REGEXES api-ms-.* ext-ms-.* # 系统 API 集转发 dll 不拷 POST_EXCLUDE_REGEXES .*[Ss]ystem32.* # 系统目录里的不拷 ) foreach(_dep IN LISTS _resolved) message(STATUS collect: ${_dep}) file(COPY ${_dep} DESTINATION ${_out}) endforeach()PRE_EXCLUDE_REGEXES过滤掉api-ms-win-crt-*这类 API 集转发 DLL它们在 Win10 上是系统自带的拷过去反而会干扰系统加载POST_EXCLUDE_REGEXES按路径过滤把System32下的都排除否则会把kernel32.dll也复制一份到输出目录——这不仅没必要某些情况下还会导致加载异常。跑完之后D:/osg/deploy里会有一批 DLL加上 exe 本身再加手工拷一个osgPlugins-3.6.5目录和share下的proj.db就是一整套可以直接分发的运行时。还有两个容易漏的东西VC 运行时msvcp140.dll、vcruntime140.dll、vcruntime140_1.dll和osgPlugins-3.6.5插件目录。前者最好让目标机装一次 VC 2015-2022 可再发行组件包别手工拷版本对不上更麻烦后者必须整目录跟着走插件是按文件名动态加载的少一个就是少一种格式支持。6.2 一个三十行的启动自检程序界面程序出问题时最难的是“它什么也没说就挂了”。我的习惯是写一个极简的控制台自检程序链接同一套库在部署前先跑一遍把问题暴露在终端里// selfcheck.cpp —— 依赖链快速自检返回 0 表示可用 #include osg/Version #include osg/Node #include osg/ref_ptr #include osgDB/Registry #include cstdio int main() { // 1. 版本对齐编译期版本和运行时 dll 版本应当一致 std::printf(OSG version: %s\n, osgGetVersion()); // 2. 插件目录打印实际搜索路径确认 OSG_LIBRARY_PATH 生效 const auto libs osgDB::Registry::instance()-getLibraryFilePathList(); for (const auto p : libs) std::printf(plugin dir: %s\n, p.c_str()); // 3. 关键插件尝试加载 osgb 读取器缺插件时这里会返回 false bool ok osgDB::Registry::instance()-loadLibrary( osgDB::Registry::instance()-createLibraryNameForExtension(osgb)); std::printf(osgdb_osg plugin: %s\n, ok ? ok : MISSING); // 4. 端到端读一个空 osgb验证插件 依赖 dll 全链路 osg::ref_ptrosg::Node n osgDB::readNodeFile(probe.osgb); std::printf(readNodeFile: %s\n, n.valid() ? ok : FAIL); return (ok n.valid()) ? 0 : 1; }对应的 CMake 只有三行add_executable(selfcheck selfcheck.cpp) # 与主程序链接同一套库才能复现相同的依赖环境 target_link_libraries(selfcheck PRIVATE osg osgDB)用法上有个小技巧先在一台干净的虚拟机里跑selfcheck.exe返回非 0 就说明部署包不完整此时去看它打印的plugin dir和MISSING行能省掉在目标机上盲猜的时间。probe.osgb不需要有任何实际内容一个空文件就够——目的不是读数据是触发插件加载和依赖解析这两步任何一步断链都会在这里暴露。从那以后我每次出包前都强制走一遍deploy.cmake加selfcheck.exe这套组合不管多急都不跳。吃过一次亏图省事手工拷了几个 DLL现场演示时程序在加载地形阶段静默退出回来查是两个小时后的事原因是漏了libzip.dll而它只在读特定压缩包时才被引用本地测试根本触发不到。希望你不用经历这一遍。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站