From 0687d76cc6be52a45c593515ea624e15357b73bb Mon Sep 17 00:00:00 2001 From: monch999 <972530504@qq.com> Date: Sun, 5 Jul 2026 23:19:56 +0800 Subject: [PATCH 1/8] Fix LAStools build failure on non-Western Windows locales LAStools sources are Windows-1252 encoded and contain non-ASCII bytes in char literals (e.g. an en dash 0x96 in lasmerge.cpp). MSVC parses source files using the system codepage by default, so on locales like Chinese (codepage 936) the byte is treated as a DBCS lead byte, swallowing the closing quote and failing the build with C2015/C2001/C2143 errors. Compile LAStools with /source-charset:.1252 and /execution-charset:.1252 so parsing matches the encoding the sources were written in, regardless of the build machine locale. Co-Authored-By: Claude Fable 5 --- SuperBuild/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SuperBuild/CMakeLists.txt b/SuperBuild/CMakeLists.txt index 967271323..67387b744 100644 --- a/SuperBuild/CMakeLists.txt +++ b/SuperBuild/CMakeLists.txt @@ -275,12 +275,18 @@ externalproject_add(fastrasterfilter ${WIN32_CMAKE_ARGS} ) +# LAStools sources are Windows-1252 encoded; on non-Western locales MSVC +# defaults to the system codepage (e.g. 936) and fails to parse them. +if (WIN32) + set(LASTOOLS_CXX_FLAGS "-DCMAKE_CXX_FLAGS=/DWIN32 /D_WINDOWS /W3 /GR /EHsc /source-charset:.1252 /execution-charset:.1252") +endif() externalproject_add(lastools GIT_REPOSITORY https://github.com/OpenDroneMap/LAStools.git GIT_TAG 250 PREFIX ${SB_BINARY_DIR}/lastools SOURCE_DIR ${SB_SOURCE_DIR}/lastools CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${SB_INSTALL_DIR} + ${LASTOOLS_CXX_FLAGS} ) externalproject_add(draco From dd71238ea3a4cd3fe73f8f30cafab0b7b8c046bc Mon Sep 17 00:00:00 2001 From: monch999 <972530504@qq.com> Date: Sun, 5 Jul 2026 23:20:34 +0800 Subject: [PATCH 2/8] Point odm_orthophoto and OpenMVS at the SuperBuild OpenCV on Windows On Windows the SuperBuild OpenCV installs its CMake config under x64/vc17/lib, but OpenMVS was given the Linux-style lib/cmake/opencv4 path (which does not exist there) and odm_orthophoto received no OpenCV_DIR at all. find_package(OpenCV) then falls back to the standard search, which derives prefixes from the PATH environment variable, so any unrelated OpenCV install on the build machine can be picked up and either fail to configure or silently link the wrong version. Pass an explicit, per-platform OpenCV_DIR to both projects, matching what External-OpenSfM.cmake already does. Co-Authored-By: Claude Fable 5 --- SuperBuild/CMakeLists.txt | 8 ++++++++ SuperBuild/cmake/External-OpenMVS.cmake | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/SuperBuild/CMakeLists.txt b/SuperBuild/CMakeLists.txt index 67387b744..86268154a 100644 --- a/SuperBuild/CMakeLists.txt +++ b/SuperBuild/CMakeLists.txt @@ -255,6 +255,13 @@ externalproject_add(dem2points ${APPLE_CMAKE_ARGS} ) +# Point find_package(OpenCV) at the SuperBuild OpenCV; otherwise a stray +# OpenCV install on the system (e.g. via PATH) can be picked up instead. +if (WIN32) + set(ORTHOPHOTO_CMAKE_ARGS "-DOpenCV_DIR=${SB_INSTALL_DIR}/x64/vc17/lib") +else() + set(ORTHOPHOTO_CMAKE_ARGS "-DOpenCV_DIR=${SB_INSTALL_DIR}/lib/cmake/opencv4") +endif() externalproject_add(odm_orthophoto DEPENDS opencv gdal GIT_REPOSITORY https://github.com/OpenDroneMap/odm_orthophoto.git @@ -262,6 +269,7 @@ externalproject_add(odm_orthophoto PREFIX ${SB_BINARY_DIR}/odm_orthophoto SOURCE_DIR ${SB_SOURCE_DIR}/odm_orthophoto CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${SB_INSTALL_DIR} + ${ORTHOPHOTO_CMAKE_ARGS} ${WIN32_CMAKE_ARGS} ) diff --git a/SuperBuild/cmake/External-OpenMVS.cmake b/SuperBuild/cmake/External-OpenMVS.cmake index c697c5dc7..4c02d981a 100644 --- a/SuperBuild/cmake/External-OpenMVS.cmake +++ b/SuperBuild/cmake/External-OpenMVS.cmake @@ -45,6 +45,12 @@ if(WIN32) file(DOWNLOAD "https://github.com/OpenDroneMap/windows-deps/releases/download/2.5.0/nvcuda_dummy.dll" "${SB_INSTALL_DIR}/bin/nvcuda.dll") endif() +if(WIN32) + set(OPENMVS_OPENCV_DIR "${SB_INSTALL_DIR}/x64/vc17/lib") +else() + set(OPENMVS_OPENCV_DIR "${SB_INSTALL_DIR}/lib/cmake/opencv4") +endif() + ExternalProject_Add(${_proj_name} DEPENDS ceres opencv vcg eigen34 PREFIX ${_SB_BINARY_DIR} @@ -59,7 +65,7 @@ ExternalProject_Add(${_proj_name} #--Configure step------------- SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name} CMAKE_ARGS - -DOpenCV_DIR=${SB_INSTALL_DIR}/lib/cmake/opencv4 + -DOpenCV_DIR=${OPENMVS_OPENCV_DIR} -DVCG_ROOT=${SB_SOURCE_DIR}/vcg -DEIGEN3_INCLUDE_DIR=${SB_SOURCE_DIR}/eigen34/ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} From 7477a6c3f665732df26c7654f133a804f7523f2b Mon Sep 17 00:00:00 2001 From: monch999 <972530504@qq.com> Date: Sun, 5 Jul 2026 23:21:33 +0800 Subject: [PATCH 3/8] Replace OpenMVS MAX_CUDA_COMPATIBILITY with an explicit arch list OpenMVS_MAX_CUDA_COMPATIBILITY derives -gencode flags by zipping the output of nvcc --list-gpu-arch and --list-gpu-code. With CUDA 12.9 the two lists no longer line up (12.9 introduced family-specific codes), so the generated flags pair mismatched entries and nvcc aborts with "Unknown arch name 'sass_90'". Turn the option off and pass CMAKE_CUDA_ARCHITECTURES explicitly, covering Pascal through Blackwell (61/75/86/89/90 real + 120 with PTX for forward compatibility), using LIST_SEPARATOR to carry the list through ExternalProject_Add. Co-Authored-By: Claude Fable 5 --- SuperBuild/cmake/External-OpenMVS.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SuperBuild/cmake/External-OpenMVS.cmake b/SuperBuild/cmake/External-OpenMVS.cmake index 4c02d981a..152cae315 100644 --- a/SuperBuild/cmake/External-OpenMVS.cmake +++ b/SuperBuild/cmake/External-OpenMVS.cmake @@ -53,6 +53,7 @@ endif() ExternalProject_Add(${_proj_name} DEPENDS ceres opencv vcg eigen34 + LIST_SEPARATOR | PREFIX ${_SB_BINARY_DIR} TMP_DIR ${_SB_BINARY_DIR}/tmp STAMP_DIR ${_SB_BINARY_DIR}/stamp @@ -71,7 +72,10 @@ ExternalProject_Add(${_proj_name} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${SB_INSTALL_DIR} -DOpenMVS_ENABLE_TESTS=OFF - -DOpenMVS_MAX_CUDA_COMPATIBILITY=ON + # MAX_CUDA_COMPATIBILITY generates invalid -gencode flags with CUDA 12.9 + # (nvcc fatal: Unknown arch name 'sass_90'); pass a valid arch list instead. + -DOpenMVS_MAX_CUDA_COMPATIBILITY=OFF + -DCMAKE_CUDA_ARCHITECTURES=61-real|75-real|86-real|89-real|90-real|120 -DINSTALL_USE_SUBDIR=OFF ${GPU_CMAKE_ARGS} ${WIN32_CMAKE_ARGS} From 73134a8947c2891aaaaee1f0afdadfb977eb0085 Mon Sep 17 00:00:00 2001 From: monch999 <972530504@qq.com> Date: Sun, 5 Jul 2026 23:22:16 +0800 Subject: [PATCH 4/8] Pass explicit python include dir and library to OpenCV on Windows OpenCV's python detection cannot always locate the base interpreter's headers and import library from a venv python on Windows (e.g. when the venv was created from an Anaconda install). PYTHON3_INCLUDE_DIR and PYTHON3_LIBRARY come up empty, PYTHON3LIBS_FOUND stays false, and the cv2 module is silently skipped -- the build succeeds but "import cv2" fails at runtime. Query the venv interpreter for its base include directory and pythonXY.lib and pass both explicitly, converting to forward slashes since backslashes break CMake string parsing in target_link_libraries. Co-Authored-By: Claude Fable 5 --- SuperBuild/cmake/External-OpenCV.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/SuperBuild/cmake/External-OpenCV.cmake b/SuperBuild/cmake/External-OpenCV.cmake index b60996312..1a7ff5402 100644 --- a/SuperBuild/cmake/External-OpenCV.cmake +++ b/SuperBuild/cmake/External-OpenCV.cmake @@ -2,9 +2,21 @@ set(_proj_name opencv) set(_SB_BINARY_DIR "${SB_BINARY_DIR}/${_proj_name}") if (WIN32) + # OpenCV cannot locate the base interpreter's headers/libs from a venv + # python on its own (detection comes up empty and cv2 is silently + # skipped), so query the interpreter and pass them explicitly. + execute_process(COMMAND ${PYTHON_EXE_PATH} -c "import sysconfig; print(sysconfig.get_paths()['include'])" + OUTPUT_VARIABLE PYTHON3_BASE_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${PYTHON_EXE_PATH} -c "import sys, os; print(os.path.join(sys.base_prefix, 'libs', 'python%d%d.lib' % sys.version_info[:2]))" + OUTPUT_VARIABLE PYTHON3_BASE_LIBRARY OUTPUT_STRIP_TRAILING_WHITESPACE) + file(TO_CMAKE_PATH "${PYTHON3_BASE_INCLUDE_DIR}" PYTHON3_BASE_INCLUDE_DIR) + file(TO_CMAKE_PATH "${PYTHON3_BASE_LIBRARY}" PYTHON3_BASE_LIBRARY) + message(STATUS "OpenCV python3: include=${PYTHON3_BASE_INCLUDE_DIR} library=${PYTHON3_BASE_LIBRARY}") set(OCV_CMAKE_EXTRA_ARGS -DPYTHON3_NUMPY_INCLUDE_DIRS=${PYTHON_HOME}/lib/site-packages/numpy/_core/include -DPYTHON3_PACKAGES_PATH=${PYTHON_HOME}/lib/site-packages -DPYTHON3_EXECUTABLE=${PYTHON_EXE_PATH} + -DPYTHON3_INCLUDE_DIR=${PYTHON3_BASE_INCLUDE_DIR} + -DPYTHON3_LIBRARY=${PYTHON3_BASE_LIBRARY} -DWITH_MSMF=OFF -DOPENCV_LIB_INSTALL_PATH=${SB_INSTALL_DIR}/lib -DOPENCV_BIN_INSTALL_PATH=${SB_INSTALL_DIR}/bin) From 30fff035ea2b1cf747da14c95f0e0ac632b245c1 Mon Sep 17 00:00:00 2001 From: monch999 <972530504@qq.com> Date: Sun, 5 Jul 2026 23:22:42 +0800 Subject: [PATCH 5/8] Disable AVIF when building OpenCV libavif is not part of the vcpkg dependency set, but OpenCV's find_package can still discover a stray copy on the build machine (e.g. Anaconda's Library tree, reachable through PATH-derived config search) and link it into opencv_imgcodecs. The build then works on the build machine but the shipped binaries fail to load on end-user machines with a missing avif.dll. Pass WITH_AVIF=OFF explicitly so the produced binaries only depend on libraries that are actually bundled. Co-Authored-By: Claude Fable 5 --- SuperBuild/cmake/External-OpenCV.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SuperBuild/cmake/External-OpenCV.cmake b/SuperBuild/cmake/External-OpenCV.cmake index 1a7ff5402..330a1c07a 100644 --- a/SuperBuild/cmake/External-OpenCV.cmake +++ b/SuperBuild/cmake/External-OpenCV.cmake @@ -72,6 +72,10 @@ ExternalProject_Add(${_proj_name} -DBUILD_opencv_legacy=ON -DBUILD_opencv_python3=ON -DWITH_FFMPEG=ON + # Keep AVIF off: a stray libavif on the build machine (e.g. Anaconda's, + # discovered via PATH) gets linked into imgcodecs and the resulting + # avif.dll dependency is missing on end-user machines. + -DWITH_AVIF=OFF -DWITH_CUDA=OFF -DWITH_GTK=OFF -DWITH_VTK=OFF From 6d78e0a163ec6c7ec2854bb89cbb21a9a92a2b62 Mon Sep 17 00:00:00 2001 From: monch999 <972530504@qq.com> Date: Tue, 4 Aug 2026 22:51:42 +0800 Subject: [PATCH 6/8] Add Chinese notes on the native Windows build Records the full native Windows build of ODM 3.6.0 on a Chinese-locale machine: environment versus what CI assumes, the build and packaging commands, and the nine problems hit along the way with their root causes and fixes. Also covers how to force-rebuild a single SuperBuild external project and how to verify an installer is self-contained. Co-Authored-By: Claude Fable 5 --- docs/windows-native-build-zh.md | 189 ++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 docs/windows-native-build-zh.md diff --git a/docs/windows-native-build-zh.md b/docs/windows-native-build-zh.md new file mode 100644 index 000000000..6096d8543 --- /dev/null +++ b/docs/windows-native-build-zh.md @@ -0,0 +1,189 @@ +# ODM Windows 原生编译记录 + +> 记录时间:2026-07-05。本文档记录在中文 Windows 11 系统上原生编译 OpenDroneMap (ODM 3.6.0) 及生成安装包的完整过程,包括遇到的所有问题、根因分析和解决办法。 + +## 1. 环境 + +### 官方要求(参照 `.github/workflows/publish-windows.yml`) + +| 依赖 | CI 使用的版本 | +|---|---| +| 操作系统 | Windows Server 2022(英文区域设置) | +| Python | 3.12.9 | +| CMake | 3.24.x | +| CUDA Toolkit | 12.8.1 | +| MSVC | VS 2022 | + +### 本机实际环境 + +| 依赖 | 本机版本 | 备注 | +|---|---|---| +| 操作系统 | Windows 11 Pro(中文区域,代码页 936/GBK) | 多个问题的根源 | +| Python | 3.12.7(Anaconda,`D:\Anaconda`) | 满足 configure.py 的 3.12.x 检查 | +| CMake | 系统装的 4.0.4(不可用)→ 便携版 3.31.6 | 见问题 1 | +| CUDA | 12.9(`CUDA_PATH` 系统未设置) | 见问题 7 | +| MSVC | VS 2022 Community 17.14(`D:\VS2022`,MSVC 14.44) | | +| Git | 2.51(`D:\Program Files (x86)\Git`) | 其 `usr\bin` 提供 patch.exe | +| GPU | RTX 4070 Ti SUPER(sm_89) | | + +## 2. 构建流程 + +ODM Windows 原生构建入口是 `python configure.py build`,内部依次执行: + +1. 创建 `venv/` 虚拟环境,安装 setuptools 和 numpy +2. 从 GitHub 下载官方预编译 vcpkg 依赖包(约 1GB,含 GDAL/Boost/CGAL 等,见 `vcpkg.json`) +3. CMake SuperBuild(`SuperBuild/CMakeLists.txt`)编译约 20 个 C++ 组件:OpenCV、GFlags、Ceres、OpenSfM、OpenMVS、PDAL、Entwine、Untwine、MvsTexturing、PoissonRecon、LAStools、Draco、PopSift(CUDA)等 +4. 用 vcpkg 的 GDAL 从源码编译 GDAL/Fiona/Rasterio 三个 Python 绑定 +5. `pip install -r requirements.txt` + +本机使用的构建脚本(`E:\tools\odm-build.cmd`): + +```bat +@echo off +call "D:\VS2022\VC\Auxiliary\Build\vcvars64.bat" +set "PATH=E:\tools\cmake-3.31.6-windows-x86_64\bin;%PATH%;D:\Program Files (x86)\Git\usr\bin" +set "CUDA_PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.9" +cd /d E:\ODM +python configure.py build +``` + +要点:先进 vcvars64 环境;便携版 CMake 放 PATH 最前(压过系统的 4.0.4);Git 的 `usr\bin` 放 PATH 最后(提供 patch.exe 又不遮蔽 MSVC 工具);显式设置 `CUDA_PATH`。 + +## 3. 编译期问题与解决 + +### 问题 1:CMake 4.x 拒绝旧的 cmake_minimum_required + +**现象**:SuperBuild 顶层 `cmake_minimum_required(VERSION 3.1)`,CMake 4.0 已移除对 <3.5 的兼容,配置直接失败;多个子项目(Ceres 2.0、MVE 等)同样受影响。 + +**解决**:不动系统 CMake,下载便携版 CMake 3.31.6 到 `E:\tools\cmake-3.31.6-windows-x86_64`,仅在构建脚本 PATH 中生效。(官方 CI 锁定 3.24.x 就是这个原因。) + +### 问题 2:LAStools 编译失败(C2015/C2001/C2143 语法错误连环报) + +**现象**:`lasmerge.cpp(131)` 等报"常量中的字符太多/常量中有换行符"。 + +**根因**:LAStools 源码是 Windows-1252 编码,字符字面量里有单字节 0x96(en-dash)。英文系统(代码页 1252)下 MSVC 把它当一个字符能编译过(官方 CI 恰好如此);中文系统(代码页 936/GBK)下 0x96 是双字节字符前导字节,把后面的引号"吃"掉导致解析错乱。 + +**解决**:`SuperBuild/CMakeLists.txt` 给 lastools 单独加编译选项: + +```cmake +set(LASTOOLS_CXX_FLAGS "-DCMAKE_CXX_FLAGS=/DWIN32 /D_WINDOWS /W3 /GR /EHsc /source-charset:.1252 /execution-charset:.1252") +``` + +### 问题 3:构建中断后重新配置,报 "could not find TARGET opencv" + +**现象**:首轮构建失败后重跑 CMake 配置,OpenSfM 的 `DEPENDS ceres opencv gflags` 找不到 opencv 目标。 + +**根因**:`SETUP_EXTERNAL_PROJECT` 宏先 `find_package`,找到已安装的库就跳过创建外部项目目标。OpenCV 首轮已编译安装,重配置时被 find_package 命中 → 目标不再创建 → 依赖它的项目悬空。SuperBuild 隐含"配置只跑一次"的假设。 + +**解决**:续跑时强制创建目标(有 stamp 文件不会真重编): + +``` +cmake .. -DCMAKE_BUILD_TYPE=Release -DODM_BUILD_OpenCV=ON -DODM_BUILD_GFlags=ON -DODM_BUILD_Ceres=ON -DCMAKE_TOOLCHAIN_FILE=...\vcpkg\scripts\buildsystems\vcpkg.cmake +``` + +另注意:`configure.py` 里只要 `SuperBuild/build` 和 `SuperBuild/install` 存在就会跳过 SuperBuild 编译,所以中断后需手动跑 `cmake --build . --config Release -j2` 完成 C++ 部分,再跑 `configure.py build` 走 Python 部分。 + +### 问题 4:Ceres 补丁步骤退出码 9009(命令找不到) + +**现象**:`ceres-patch.rule` 自定义生成退出码 9009。 + +**根因**:`External-Ceres.cmake` 的 `PATCH_COMMAND patch -p1 < ceres.patch` 用的是 Unix `patch` 工具。GitHub CI 的 Windows runner 自带 Git 的 `usr\bin` 在 PATH 里,本机默认没有。 + +**解决**:把 `D:\Program Files (x86)\Git\usr\bin`(内含 patch.exe)追加到构建 PATH **末尾**(避免其中的 Unix 同名工具遮蔽 MSVC)。 + +### 问题 5:odm_orthophoto 配置失败,找到错误的 OpenCV + +**现象**:`find_package(OpenCV)` 命中 `D:\3rdParty\opencv-4.8.0\build`(本机自装的旧 OpenCV),报"没有兼容的二进制"。 + +**根因**:odm_orthophoto 没有传 `OpenCV_DIR`;OpenMVS 在 Windows 上传的是 Linux 风格路径 `lib/cmake/opencv4`(不存在)。CMake 的 config 搜索会从 PATH 环境变量反推安装前缀,本机 PATH 里恰好有旧 OpenCV 的目录。 + +**解决**:像 OpenSfM 一样按平台显式传 `OpenCV_DIR`(Windows 上 SuperBuild OpenCV 的 config 在 `install/x64/vc17/lib`)。 + +### 问题 6:OpenMVS 的 CUDA 编译失败 `nvcc fatal: Unknown arch name 'sass_90'` + +**根因**:`OpenMVS_MAX_CUDA_COMPATIBILITY=ON` 时,OpenMVS 用 `nvcc --list-gpu-arch` 和 `--list-gpu-code` 的输出逐项配对生成 `-gencode`。CUDA 12.9 引入 family-specific code 后两个列表长度/顺序不再对应,配对出 `arch=compute_100,code=sass_90` 这类非法参数。官方 CI 用 12.8.1 未触发。 + +**解决**:关掉 max-compat,显式指定架构列表(经 `LIST_SEPARATOR |` 传入外部项目): + +```cmake +-DOpenMVS_MAX_CUDA_COMPATIBILITY=OFF +-DCMAKE_CUDA_ARCHITECTURES=61-real|75-real|86-real|89-real|90-real|120 +``` + +覆盖 GTX 10 系到 RTX 50 系,末位 120 带 PTX 向前兼容。 + +### 问题 7:`import cv2` 失败——OpenCV 静默跳过了 Python 绑定 + +**现象**:SuperBuild 全部编完、requirements 装完后,venv 里没有 cv2。OpenCV 配置摘要显示 `Unavailable: ... python3`。 + +**根因**(三层连环): +1. venv 基于 Anaconda Python 创建,OpenCV 的检测逻辑无法从 venv 的 python.exe 反查到基础解释器的 `Include\` 和 `libs\python312.lib`,`PYTHON3LIBS_FOUND` 为空 → cv2 模块被**静默**跳过(构建照样成功); +2. 修复传参后仍不生效——OpenCV `find_python()` 开头 `if(NOT ${found})`,上次失败留下的 `PYTHON3INTERP_FOUND=TRUE` INTERNAL 缓存把整个检测短路了,必须删掉 `build/opencv/CMakeCache.txt` 强制全新检测; +3. 再次重配后编译报 `Invalid character escape '\A'`——传入的 `D:\Anaconda\...` 反斜杠路径在 `target_link_libraries` 的字符串解析中被当转义符,需 `file(TO_CMAKE_PATH)` 转正斜杠。 + +**解决**:`External-OpenCV.cmake` Windows 分支用 `execute_process` 向 venv 解释器动态查询 include 目录和 `pythonXY.lib` 路径,转正斜杠后显式传 `-DPYTHON3_INCLUDE_DIR` / `-DPYTHON3_LIBRARY`。 + +**强制重编单个外部项目的方法**:删 `SuperBuild\build\<项目>\stamp\Release\` 下的 `*-configure`、`*-build`、`*-install`、`*-done` 戳记;如需重跑其内部 CMake 检测,还要删该项目构建目录的 `CMakeCache.txt`。 + +## 4. 安装包生成与可移植性问题 + +### 生成方法 + +``` +python configure.py dist +``` + +自动下载 VC++ 运行库、便携版 Python 3.12.9(embed 版)、Inno Setup,产出 `dist\ODM_Setup_3.6.0.exe`(约 234MB,未签名)。安装包自带 Python,装机时 `winpostinstall.bat` 把 venv 的 `pyvenv.cfg` 指向内嵌解释器,理论上不依赖目标机任何环境。 + +### 问题 8:装到"干净"目录后 `import ctypes` 失败(DLL load failed) + +**现象**:静默安装测试,`run.py` 起步就挂在 `vmem → ctypes`。sys.path 里赫然出现 `D:\Anaconda\Lib` 和 `D:\Anaconda\DLLs`,`_ctypes.pyd` 从 Anaconda 加载(其依赖的 ffi DLL 不在搜索路径)。 + +**根因**:官方内嵌 Python 用的是删除了 `._pth` 的 embed 版("less-pth",为了让 pyvenv.cfg 生效)。副作用:没有 `._pth` 时 python.exe 会读取注册表 `HKLM/HKCU\Software\Python\PythonCore\3.12\PythonPath` 并合并进 sys.path。本机 Anaconda 注册了这个键。**任何装有 Python 3.12 的最终用户机器都会踩**(官方安装包同样潜伏此问题)。 + +**解决**:`winpostinstall.bat` 安装时在内嵌 python.exe 旁写 `python312._pth`: + +``` +python312.zip +. +..\.. +..\Lib\site-packages +import site +``` + +`._pth` 存在时解释器完全忽略注册表和 PYTHON* 环境变量,sys.path 被锁定为列出的相对路径(stdlib zip、扩展模块目录、ODM 根目录、venv site-packages),任何机器行为一致。相比给 `run.bat` 加 `python -E`,此法覆盖所有 python 调用(OpenSfM 的 `opensfm.bat` 等子进程也是裸调 `python`)。 + +### 问题 9:装到"干净"目录后 `import cv2` 失败(缺 avif.dll) + +**现象**:`opencv_imgcodecs4120.dll` 及依赖它的 highgui/videoio 加载失败。`dumpbin /dependents` 显示 imgcodecs 依赖 `avif.dll`——不在打包产物里,只在 `D:\Anaconda\Library\bin` 有。 + +**根因**:OpenCV 配置时 `find_package` 经 PATH 反推发现了 Anaconda 的 libavif 并链了进去(vcpkg 依赖集里本无 libavif)。开发机上 Anaconda 的 DLL 兜底所以一切正常,装到没有 Anaconda 的机器立刻断链。典型的"构建机环境污染"问题。 + +**解决**:`External-OpenCV.cmake` 显式 `-DWITH_AVIF=OFF`,与官方依赖集一致;重编 OpenCV。 + +### 验证方法(建议每次打包后执行) + +1. 静默安装到临时目录:`ODM_Setup_x.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /DIR=<临时目录>` +2. 在安装目录环境下验证导入:`ctypes、cv2、osgeo.gdal、fiona、rasterio` +3. `python run.py --help` 冒烟测试 +4. **全量依赖扫描**(脚本 `E:\tools\scan_deps.ps1`):对安装树全部 DLL/pyd/exe 用 `dumpbin /dependents` 提取导入表,逐项核对"存在于安装树内 或 是 System32 系统库",任何落空项即为可移植性缺陷。本次最终版扫描 653 个 PE 文件,零未解析依赖。 +5. `unins000.exe /VERYSILENT` 卸载清理 + +## 5. 修复提交清单(分支 `windows-build-fixes`) + +| 提交 | 内容 | 上游价值 | +|---|---|---| +| `Fix LAStools build failure on non-Western Windows locales` | 问题 2 | 高(所有非西文系统构建者) | +| `Point odm_orthophoto and OpenMVS at the SuperBuild OpenCV on Windows` | 问题 5 | 高 | +| `Replace OpenMVS MAX_CUDA_COMPATIBILITY with an explicit arch list` | 问题 6 | 高(CUDA ≥12.9 均触发) | +| `Pass explicit python include dir and library to OpenCV on Windows` | 问题 7 | 中(venv 基于非 python.org 解释器时) | +| `Disable AVIF when building OpenCV` | 问题 9 | 高(构建机污染防御) | + +问题 8 的修复(`winpostinstall.bat` 写 `._pth`)不在本分支,它由分支 +`fix-embedded-python-registry-pythonpath` 的提交 +`Prevent registry PythonPath from polluting sys.path on Windows installs` +承载,并已单独提交上游 PR。**注意:本分支的工作区不含该修复**,若要从本分支 +重新打安装包,需先合并或 cherry-pick 那个提交,否则产出的安装包在装有其他 +Python 3.12 的机器上会因注册表 PythonPath 污染而无法启动。 + +问题 1/3/4 属于构建环境准备,不涉及仓库改动,已记录在上文和构建脚本中。 From ef68647881460c5294f538f9f8b3e717e5214ead Mon Sep 17 00:00:00 2001 From: monch999 <972530504@qq.com> Date: Mon, 6 Jul 2026 09:28:34 +0800 Subject: [PATCH 7/8] Prevent registry PythonPath from polluting sys.path on Windows installs The Windows setup ships an embedded Python build with its ._pth file removed (so that pyvenv.cfg is honored). A side effect is that without a ._pth, python.exe falls back to reading HKLM/HKCU\Software\Python\PythonCore\3.12\PythonPath from the registry and merges those entries into sys.path. On any end-user machine that has a registered Python 3.12 install (python.org, Anaconda, etc.), the registered Lib/ and DLLs/ directories take part in module resolution, so stdlib extension modules such as _ctypes.pyd get loaded from the foreign install while their dependent DLLs are not on the search path. ODM then crashes on startup with "ImportError: DLL load failed" as soon as opendm.vmem imports ctypes. Fix: write a python312._pth next to the embedded python.exe during post-install. When a ._pth is present, sys.path is fully defined by the file and both registry entries and PYTHONPATH/PYTHONHOME are ignored. The file lists the embedded stdlib zip, the embed directory itself, the ODM install root and the venv site-packages, which is exactly the set of paths ODM needs. Co-Authored-By: Claude Fable 5 --- winpostinstall.bat | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/winpostinstall.bat b/winpostinstall.bat index adaf520c9..85d087d8f 100644 --- a/winpostinstall.bat +++ b/winpostinstall.bat @@ -8,6 +8,16 @@ set PYENVCFG=%WRITABLE_VIRTUAL_ENV%\pyvenv.cfg echo home = %VIRTUAL_ENV%\Scripts> "%PYENVCFG%" echo include-system-site-packages = false>> "%PYENVCFG%" +rem Write python312._pth next to the embedded python.exe to lock down sys.path. +rem Without it, the "less-pth" embedded python merges registry PythonPath entries +rem from any system Python 3.12 (e.g. Anaconda), breaking ctypes/DLL loading. +set PTHFILE=%VIRTUAL_ENV%\Scripts\python312._pth +echo python312.zip> "%PTHFILE%" +echo .>> "%PTHFILE%" +echo ..\..>> "%PTHFILE%" +echo ..\Lib\site-packages>> "%PTHFILE%" +echo import site>> "%PTHFILE%" + rem Hot-patching cv2 extension configs set SBBIN=%ODMBASE%SuperBuild\install\bin set CV2=%WRITABLE_VIRTUAL_ENV%\Lib\site-packages\cv2 From b1534eb2287875e81f1b4e1da267e5fbe9d5f6fd Mon Sep 17 00:00:00 2001 From: monch999 <972530504@qq.com> Date: Tue, 4 Aug 2026 23:08:43 +0800 Subject: [PATCH 8/8] Note the cherry-picked ._pth fix in the Windows build notes The registry PythonPath fix now rides on this branch as a cherry-pick of the commit that carries the upstream PR, so the earlier warning about packaging from this branch no longer applies. Co-Authored-By: Claude Fable 5 --- docs/windows-native-build-zh.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/windows-native-build-zh.md b/docs/windows-native-build-zh.md index 6096d8543..acd3d28b9 100644 --- a/docs/windows-native-build-zh.md +++ b/docs/windows-native-build-zh.md @@ -178,12 +178,11 @@ import site | `Replace OpenMVS MAX_CUDA_COMPATIBILITY with an explicit arch list` | 问题 6 | 高(CUDA ≥12.9 均触发) | | `Pass explicit python include dir and library to OpenCV on Windows` | 问题 7 | 中(venv 基于非 python.org 解释器时) | | `Disable AVIF when building OpenCV` | 问题 9 | 高(构建机污染防御) | +| `Prevent registry PythonPath from polluting sys.path on Windows installs` | 问题 8 | 高(影响所有装了 Python 3.12 的最终用户) | -问题 8 的修复(`winpostinstall.bat` 写 `._pth`)不在本分支,它由分支 -`fix-embedded-python-registry-pythonpath` 的提交 -`Prevent registry PythonPath from polluting sys.path on Windows installs` -承载,并已单独提交上游 PR。**注意:本分支的工作区不含该修复**,若要从本分支 -重新打安装包,需先合并或 cherry-pick 那个提交,否则产出的安装包在装有其他 -Python 3.12 的机器上会因注册表 PythonPath 污染而无法启动。 +问题 8 的修复由分支 `fix-embedded-python-registry-pythonpath`(提交 `2a258cb0`) +承载并已单独提交上游 PR;本分支是从那里 cherry-pick 过来的同一份改动,因此从本 +分支直接打包即可得到正确的安装包。上游 PR 合并后再 rebase 本分支时,git 会通过 +patch-id 识别该提交已应用并自动丢弃,不会产生冲突。 问题 1/3/4 属于构建环境准备,不涉及仓库改动,已记录在上文和构建脚本中。