From 937a406003aaca543e4319b3f60c9a8e675007c1 Mon Sep 17 00:00:00 2001 From: MaxRev Date: Fri, 25 Sep 2026 00:45:13 +0300 Subject: [PATCH 1/6] fix(win): build hdf5, netcdf-c and cfitsio from vcpkg instead of the GisInternals SDK (#246) The Windows runtime package inherited hdf5.dll (HDF5 1.12.0), netcdf.dll (netCDF-C 4.8.1) and cfitsio.dll (CFITSIO 3.49) from the prebuilt GisInternals SDK (release-1930-x64). All three predate fixes for families of CVEs reported in #246. Linux and macOS already build these from vcpkg and resolve to HDF5 2.2.0, netCDF-C 4.9.3 and CFITSIO 4.7.0. Add cfitsio, hdf5 and netcdf-c to the windows-dynamic feature so vcpkg supplies them, pin HDF5_ROOT / netCDF_ROOT / CFITSIO_ROOT to the vcpkg prefix (searched ahead of CMAKE_PREFIX_PATH, which also contains the SDK), and set GDAL_USE_HDF5=ON, GDAL_USE_NETCDF=ON, GDAL_USE_CFITSIO=ON so a resolution miss fails the configure rather than silently dropping the drivers. Copy-DependentDLLs falls back to the SDK bin directory, so a failed resolution would repackage the old DLLs with CI still green. Assert after collection that the packaged hdf5.dll, hdf5_hl.dll, netcdf.dll and cfitsio.dll are byte-identical to the vcpkg builds. HDF4 (hdf.dll, mfhdf.dll, xdr.dll, szip.dll) intentionally stays from the SDK - it has no vcpkg port. szip DLL clash: the SDK ships szip.dll for HDF4, and vcpkg's default hdf5[szip] feature pulls libaec which also produces szip.dll. To avoid a wrong-ABI collision, hdf5 is installed with default-features off (zlib only); netcdf-c's transitive dependency on hdf5 also specifies default-features: false, so szip is never pulled in and HDF4 keeps the SDK's original szip.dll. --- shared/vcpkg.json | 9 +++++++++ win/partials.psm1 | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/shared/vcpkg.json b/shared/vcpkg.json index 8756987b2..d35bda79c 100644 --- a/shared/vcpkg.json +++ b/shared/vcpkg.json @@ -240,6 +240,15 @@ ] }, "libpq", + "cfitsio", + { + "name": "hdf5", + "default-features": false, + "features": [ + "zlib" + ] + }, + "netcdf-c", "libiconv", "lz4", "liblzma", diff --git a/win/partials.psm1 b/win/partials.psm1 index d43da12f4..b5e2b8999 100644 --- a/win/partials.psm1 +++ b/win/partials.psm1 @@ -344,6 +344,19 @@ function Build-Gdal { # check after Get-CollectDeps is what catches "found the SDK copy instead". $env:PG_ROOT_ARG = "-DPostgreSQL_ROOT=$env:VCPKG_INSTALLED" + # HDF5, netCDF-C and CFITSIO must resolve from vcpkg, not from the GisInternals + # SDK (see issue #246). Pin roots so the SDK copies in CMAKE_PREFIX_PATH are not + # picked up. HDF4 (hdf.dll, mfhdf.dll, xdr.dll, szip.dll) intentionally stays + # from the SDK - it has no vcpkg port. + # + # hdf5 is installed WITHOUT the default szip feature to avoid a DLL name clash: + # the SDK ships szip.dll for HDF4, and vcpkg's szip feature pulls libaec which + # also produces szip.dll. Keeping szip off lets HDF4 use the SDK's original + # szip.dll while HDF5 still compresses via zlib. + $env:HDF5_ROOT_ARG = "-DHDF5_ROOT=$env:VCPKG_INSTALLED" + $env:NETCDF_ROOT_ARG = "-DnetCDF_ROOT=$env:VCPKG_INSTALLED" + $env:CFITSIO_ROOT_ARG = "-DCFITSIO_ROOT=$env:VCPKG_INSTALLED" + if (Test-WindowsBuildReuse -Component "gdal" ` -RequiredPaths @( "$env:GDAL_INSTALL_DIR\bin\gdal.dll", @@ -419,6 +432,12 @@ function Build-Gdal { $env:Poppler_INCLUDE_DIR $env:Poppler_LIBRARY ` $env:PG_ROOT_ARG ` -DGDAL_USE_POSTGRESQL=ON ` + $env:HDF5_ROOT_ARG ` + $env:NETCDF_ROOT_ARG ` + $env:CFITSIO_ROOT_ARG ` + -DGDAL_USE_HDF5=ON ` + -DGDAL_USE_NETCDF=ON ` + -DGDAL_USE_CFITSIO=ON ` -DGDAL_USE_KEA=OFF ` -DGDAL_USE_ZLIB_INTERNAL=ON ` -DGDAL_CSHARP_APPS=ON ` @@ -490,6 +509,18 @@ function Build-CsharpBindings { } Write-BuildStep "Verified packaged libpq.dll comes from vcpkg" + # Same guard for HDF5, netCDF and CFITSIO (#246). Copy-DependentDLLs falls back + # to SDK_PREFIX\bin, which still carries the old SDK builds. + foreach ($dllName in @("hdf5.dll", "hdf5_hl.dll", "netcdf.dll", "cfitsio.dll")) { + $vcpkgDll = "$env:VCPKG_INSTALLED\bin\$dllName" + $packagedDll = Join-Path "$outputPath" $dllName + if (-not (Test-Path $vcpkgDll) -or -not (Test-Path $packagedDll) -or + (Get-FileHash $packagedDll).Hash -ne (Get-FileHash $vcpkgDll).Hash) { + throw "Packaged $dllName is not the vcpkg build. Expected a copy of $vcpkgDll (see #246)." + } + } + Write-BuildStep "Verified packaged HDF5, netCDF and CFITSIO DLLs come from vcpkg" + Build-GenerateProjectFiles -packageVersion $packageVersion -preRelease $preRelease Set-Location $PSScriptRoot From 8fe2f5839762c3b5225b65db1714b1a64abda1c0 Mon Sep 17 00:00:00 2001 From: MaxRev Date: Fri, 25 Sep 2026 00:51:19 +0300 Subject: [PATCH 2/6] fix(win): re-enable hdf5 szip via libaec, make hl feature explicit Reverses the szip-off decision: dropping szip means Windows cannot read szip-compressed HDF5/netCDF-4 files that Linux and macOS can (both use hdf5 with default features including szip via libaec). libaec 1.1.7 on MSVC produces aec.dll + szip.dll (OUTPUT_NAME "szip" in src/CMakeLists.txt). The szip.dll is a drop-in replacement exporting the SZ_* API that the SDK HDF4 (hdf.dll, mfhdf.dll) imports - the same arrangement Debian uses for libhdf4 + libaec. Because VCPKG_INSTALLED\bin is searched before SDK_PREFIX\bin in Copy-DependentDLLs, the vcpkg szip.dll supersedes the SDK copy and serves both HDF5 and HDF4 at runtime. Also makes the hl feature explicit on hdf5 in vcpkg.json; hdf5 2.x does not build hl by default and the hdf5_hl.dll assertion relied solely on netcdf-c pulling it in transitively. Adds szip.dll and aec.dll to the byte-identical hash assertions. --- shared/vcpkg.json | 3 +-- win/partials.psm1 | 16 +++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/shared/vcpkg.json b/shared/vcpkg.json index d35bda79c..31448283e 100644 --- a/shared/vcpkg.json +++ b/shared/vcpkg.json @@ -243,9 +243,8 @@ "cfitsio", { "name": "hdf5", - "default-features": false, "features": [ - "zlib" + "hl" ] }, "netcdf-c", diff --git a/win/partials.psm1 b/win/partials.psm1 index b5e2b8999..e80dbc0bf 100644 --- a/win/partials.psm1 +++ b/win/partials.psm1 @@ -346,13 +346,15 @@ function Build-Gdal { # HDF5, netCDF-C and CFITSIO must resolve from vcpkg, not from the GisInternals # SDK (see issue #246). Pin roots so the SDK copies in CMAKE_PREFIX_PATH are not - # picked up. HDF4 (hdf.dll, mfhdf.dll, xdr.dll, szip.dll) intentionally stays - # from the SDK - it has no vcpkg port. + # picked up. HDF4 (hdf.dll, mfhdf.dll, xdr.dll) intentionally stays from the + # SDK - it has no vcpkg port. # - # hdf5 is installed WITHOUT the default szip feature to avoid a DLL name clash: - # the SDK ships szip.dll for HDF4, and vcpkg's szip feature pulls libaec which - # also produces szip.dll. Keeping szip off lets HDF4 use the SDK's original - # szip.dll while HDF5 still compresses via zlib. + # hdf5 is installed WITH szip (default feature, via libaec). On MSVC, libaec + # produces aec.dll + szip.dll; the latter replaces the SDK's szip.dll in the + # package because VCPKG_INSTALLED\bin is searched before SDK_PREFIX\bin. This + # is safe: libaec's szip.dll is a drop-in implementing the SZ_* API that the + # SDK HDF4 (hdf.dll / mfhdf.dll) imports, matching what Debian does with + # libhdf4 + libaec. $env:HDF5_ROOT_ARG = "-DHDF5_ROOT=$env:VCPKG_INSTALLED" $env:NETCDF_ROOT_ARG = "-DnetCDF_ROOT=$env:VCPKG_INSTALLED" $env:CFITSIO_ROOT_ARG = "-DCFITSIO_ROOT=$env:VCPKG_INSTALLED" @@ -511,7 +513,7 @@ function Build-CsharpBindings { # Same guard for HDF5, netCDF and CFITSIO (#246). Copy-DependentDLLs falls back # to SDK_PREFIX\bin, which still carries the old SDK builds. - foreach ($dllName in @("hdf5.dll", "hdf5_hl.dll", "netcdf.dll", "cfitsio.dll")) { + foreach ($dllName in @("hdf5.dll", "hdf5_hl.dll", "netcdf.dll", "cfitsio.dll", "szip.dll", "aec.dll")) { $vcpkgDll = "$env:VCPKG_INSTALLED\bin\$dllName" $packagedDll = Join-Path "$outputPath" $dllName if (-not (Test-Path $vcpkgDll) -or -not (Test-Path $packagedDll) -or From 4cbe06708528827c55e95289937c55e0dd5512ba Mon Sep 17 00:00:00 2001 From: MaxRev Date: Fri, 25 Sep 2026 02:22:15 +0300 Subject: [PATCH 3/6] fix(win): drop HDF5_ROOT to fix FindHDF5 with HDF5 2.x CMake's FindHDF5 module, when given HDF5_ROOT, finds the HDF5 2.x config file and reads the version (2.1.1) but does not populate the legacy HDF5_INCLUDE_DIRS variable, causing GDAL's configure to fail with "Could NOT find HDF5 (missing: HDF5_INCLUDE_DIRS)". Drop HDF5_ROOT and rely on CMAKE_PREFIX_PATH ordering (vcpkg is listed before the SDK), matching the linux build which uses the same HDF5 2.x from vcpkg without HDF5_ROOT. GDAL_USE_HDF5=ON still catches a total miss, and the byte-identical DLL assertion catches accidental SDK resolution at package time. --- win/partials.psm1 | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/win/partials.psm1 b/win/partials.psm1 index e80dbc0bf..e1b708499 100644 --- a/win/partials.psm1 +++ b/win/partials.psm1 @@ -345,9 +345,8 @@ function Build-Gdal { $env:PG_ROOT_ARG = "-DPostgreSQL_ROOT=$env:VCPKG_INSTALLED" # HDF5, netCDF-C and CFITSIO must resolve from vcpkg, not from the GisInternals - # SDK (see issue #246). Pin roots so the SDK copies in CMAKE_PREFIX_PATH are not - # picked up. HDF4 (hdf.dll, mfhdf.dll, xdr.dll) intentionally stays from the - # SDK - it has no vcpkg port. + # SDK (see issue #246). HDF4 (hdf.dll, mfhdf.dll, xdr.dll) intentionally stays + # from the SDK - it has no vcpkg port. # # hdf5 is installed WITH szip (default feature, via libaec). On MSVC, libaec # produces aec.dll + szip.dll; the latter replaces the SDK's szip.dll in the @@ -355,7 +354,13 @@ function Build-Gdal { # is safe: libaec's szip.dll is a drop-in implementing the SZ_* API that the # SDK HDF4 (hdf.dll / mfhdf.dll) imports, matching what Debian does with # libhdf4 + libaec. - $env:HDF5_ROOT_ARG = "-DHDF5_ROOT=$env:VCPKG_INSTALLED" + # + # HDF5_ROOT is deliberately NOT set: CMake's FindHDF5 with HDF5_ROOT finds the + # HDF5 2.x config file and reads the version, but does not populate the legacy + # HDF5_INCLUDE_DIRS variable, failing GDAL's configure. The linux build has the + # same HDF5 2.x from vcpkg and works without HDF5_ROOT, relying on + # CMAKE_PREFIX_PATH ordering (vcpkg is listed before the SDK). The packaged-DLL + # assertion below catches any accidental SDK resolution. $env:NETCDF_ROOT_ARG = "-DnetCDF_ROOT=$env:VCPKG_INSTALLED" $env:CFITSIO_ROOT_ARG = "-DCFITSIO_ROOT=$env:VCPKG_INSTALLED" @@ -434,7 +439,6 @@ function Build-Gdal { $env:Poppler_INCLUDE_DIR $env:Poppler_LIBRARY ` $env:PG_ROOT_ARG ` -DGDAL_USE_POSTGRESQL=ON ` - $env:HDF5_ROOT_ARG ` $env:NETCDF_ROOT_ARG ` $env:CFITSIO_ROOT_ARG ` -DGDAL_USE_HDF5=ON ` From 356b86a0ea9f60cedcc5cd344055a642b8847597 Mon Sep 17 00:00:00 2001 From: MaxRev Date: Fri, 25 Sep 2026 03:42:20 +0300 Subject: [PATCH 4/6] fix(win): force HDF5 to resolve entirely from vcpkg, add cpp feature Root cause: without HDF5_ROOT, FindHDF5 mixes SDK and vcpkg - it found SDK headers (version 1.12.0) but vcpkg hdf5.lib (2.x), plus SDK hdf5_cpp.lib because the cpp feature was missing from vcpkg. GDAL requests HDF5 CXX because KEA is detected from the SDK even with GDAL_USE_KEA=OFF (HAVE_KEA stays true). With HDF5_ROOT alone, FindHDF5 finds the 2.x config file but HDF5_INCLUDE_DIRS stays empty: HDF5 2.x's hdf5-config.cmake uses a lowercase "hdf5_" prefix for its variables that FindHDF5 does not map to the expected uppercase "HDF5_INCLUDE_DIR". Fix: set HDF5_ROOT to restrict library search to vcpkg, pre-set HDF5_C_INCLUDE_DIR and HDF5_CXX_INCLUDE_DIR as cache variables to bypass the broken extraction, and add the cpp feature so the CXX component resolves from vcpkg. Add a CMakeCache.txt post-configure check that fails the build if any HDF5/netCDF/CFITSIO cache entry points under the SDK prefix. Add hdf5_cpp.dll to the byte-identical DLL assertions. --- shared/vcpkg.json | 1 + win/partials.psm1 | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/shared/vcpkg.json b/shared/vcpkg.json index 31448283e..47a9a54f2 100644 --- a/shared/vcpkg.json +++ b/shared/vcpkg.json @@ -244,6 +244,7 @@ { "name": "hdf5", "features": [ + "cpp", "hl" ] }, diff --git a/win/partials.psm1 b/win/partials.psm1 index e1b708499..c7075c8d7 100644 --- a/win/partials.psm1 +++ b/win/partials.psm1 @@ -348,19 +348,24 @@ function Build-Gdal { # SDK (see issue #246). HDF4 (hdf.dll, mfhdf.dll, xdr.dll) intentionally stays # from the SDK - it has no vcpkg port. # - # hdf5 is installed WITH szip (default feature, via libaec). On MSVC, libaec - # produces aec.dll + szip.dll; the latter replaces the SDK's szip.dll in the - # package because VCPKG_INSTALLED\bin is searched before SDK_PREFIX\bin. This + # hdf5 is installed WITH szip (default feature, via libaec) and the cpp feature + # (GDAL requests CXX when KEA is detected, even with GDAL_USE_KEA=OFF). On + # MSVC, libaec produces aec.dll + szip.dll; the latter replaces the SDK's + # szip.dll because VCPKG_INSTALLED\bin is searched before SDK_PREFIX\bin. This # is safe: libaec's szip.dll is a drop-in implementing the SZ_* API that the # SDK HDF4 (hdf.dll / mfhdf.dll) imports, matching what Debian does with # libhdf4 + libaec. # - # HDF5_ROOT is deliberately NOT set: CMake's FindHDF5 with HDF5_ROOT finds the - # HDF5 2.x config file and reads the version, but does not populate the legacy - # HDF5_INCLUDE_DIRS variable, failing GDAL's configure. The linux build has the - # same HDF5 2.x from vcpkg and works without HDF5_ROOT, relying on - # CMAKE_PREFIX_PATH ordering (vcpkg is listed before the SDK). The packaged-DLL - # assertion below catches any accidental SDK resolution. + # HDF5_ROOT restricts FindHDF5 to vcpkg. Without it, FindHDF5 mixes SDK + # headers (1.12.0) with vcpkg libraries (2.x) - an ABI-incompatible combination. + # HDF5_ROOT alone makes FindHDF5 find the 2.x config file and read the version, + # but HDF5 2.x's hdf5-config.cmake sets variables with a lowercase "hdf5_" + # prefix that FindHDF5 doesn't map to the expected uppercase "HDF5_INCLUDE_DIR", + # leaving HDF5_INCLUDE_DIRS empty. Pre-setting HDF5_C_INCLUDE_DIR and + # HDF5_CXX_INCLUDE_DIR as cache variables bypasses the broken extraction. + $env:HDF5_ROOT_ARG = "-DHDF5_ROOT=$env:VCPKG_INSTALLED" + $env:HDF5_C_INCLUDE_ARG = "-DHDF5_C_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" + $env:HDF5_CXX_INCLUDE_ARG = "-DHDF5_CXX_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" $env:NETCDF_ROOT_ARG = "-DnetCDF_ROOT=$env:VCPKG_INSTALLED" $env:CFITSIO_ROOT_ARG = "-DCFITSIO_ROOT=$env:VCPKG_INSTALLED" @@ -439,6 +444,9 @@ function Build-Gdal { $env:Poppler_INCLUDE_DIR $env:Poppler_LIBRARY ` $env:PG_ROOT_ARG ` -DGDAL_USE_POSTGRESQL=ON ` + $env:HDF5_ROOT_ARG ` + $env:HDF5_C_INCLUDE_ARG ` + $env:HDF5_CXX_INCLUDE_ARG ` $env:NETCDF_ROOT_ARG ` $env:CFITSIO_ROOT_ARG ` -DGDAL_USE_HDF5=ON ` @@ -453,6 +461,17 @@ function Build-Gdal { -DBUILD_JAVA_BINDINGS=OFF ` -DBUILD_PYTHON_BINDINGS=OFF + # Fail early if cmake resolved any HDF5, netCDF or CFITSIO path from the SDK + # instead of vcpkg (#246). Catches mixed-ABI configurations that the DLL + # assertions alone cannot detect (e.g. SDK headers with vcpkg libraries). + $cacheFile = "$env:GdalCmakeBuild\CMakeCache.txt" + $leaked = Select-String -Path $cacheFile -Pattern "^(HDF5|netCDF|CFITSIO).*=.*$([regex]::Escape($env:SDK))" | + Where-Object { $_.Line -notmatch "^#" } + if ($leaked) { + $leaked | ForEach-Object { Write-BuildError $_.Line } + throw "CMake resolved an HDF5/netCDF/CFITSIO path from the SDK (see #246)." + } + Write-BuildStep "Verified HDF5/netCDF/CFITSIO cache entries point to vcpkg" Write-BuildStep "Building GDAL" exec { cmake --build . -j $env:CMAKE_PARALLEL_JOBS --config Release --target install } @@ -517,7 +536,7 @@ function Build-CsharpBindings { # Same guard for HDF5, netCDF and CFITSIO (#246). Copy-DependentDLLs falls back # to SDK_PREFIX\bin, which still carries the old SDK builds. - foreach ($dllName in @("hdf5.dll", "hdf5_hl.dll", "netcdf.dll", "cfitsio.dll", "szip.dll", "aec.dll")) { + foreach ($dllName in @("hdf5.dll", "hdf5_hl.dll", "hdf5_cpp.dll", "netcdf.dll", "cfitsio.dll", "szip.dll", "aec.dll")) { $vcpkgDll = "$env:VCPKG_INSTALLED\bin\$dllName" $packagedDll = Join-Path "$outputPath" $dllName if (-not (Test-Path $vcpkgDll) -or -not (Test-Path $packagedDll) -or From c80e8e18d2aabc5550e760c6b631c145f3e0b355 Mon Sep 17 00:00:00 2001 From: "Camille-Maxime Thea (MaxRev)" Date: Fri, 25 Sep 2026 05:35:00 +0300 Subject: [PATCH 5/6] fix(win): don't require hdf5_cpp.dll in the package gdal.dll links the vcpkg HDF5 C++ library but imports no symbols from it, so Copy-DependentDLLs never collects hdf5_cpp.dll and the mandatory byte-identity check failed on a file that is correctly absent. Check it only when it is packaged. --- win/partials.psm1 | 1518 +++++++++++++++++++++++---------------------- 1 file changed, 763 insertions(+), 755 deletions(-) diff --git a/win/partials.psm1 b/win/partials.psm1 index c7075c8d7..5f776c081 100644 --- a/win/partials.psm1 +++ b/win/partials.psm1 @@ -1,755 +1,763 @@ -function Set-GdalVariables { - $sharedConfig = Get-ForceResolvePath "$PSScriptRoot\..\shared\GdalCore.opt" - $gdalVersion = (Select-String -Path $sharedConfig -Pattern '^GDAL_VERSION=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() - $projVersion = (Select-String -Path $sharedConfig -Pattern '^PROJ_VERSION=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() - $vcpkgCommit = (Select-String -Path $sharedConfig -Pattern '^VCPKG_COMMIT_VER=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() - - $env:GDAL_VERSION = $gdalVersion - $env:GDAL_COMMIT_VER = "v$gdalVersion" - $env:PROJ_VERSION = $projVersion - $env:PROJ_COMMIT_VER = $projVersion - $env:VCPKG_COMMIT_VER = $vcpkgCommit - - $env:VS_VERSION = "Visual Studio 17 2022" - $env:SDK = "release-1930-x64" #2022 x64 - $env:SDK_ZIP = "$env:SDK" + "-dev.zip" - $env:SDK_URL = "http://download.gisinternals.com/sdk/downloads/$env:SDK_ZIP" - $env:LIBWEBP_URL = "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-windows-x64.zip" - $env:LIBZSTD_URL = "https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-v1.5.5-win64.zip" - $env:LIBDEFLATE_URL = "https://github.com/ebiggers/libdeflate/releases/download/v1.19/libdeflate-1.19-windows-x86_64-bin.zip" - $env:VS_VER = "2022" - $env:ARCHITECTURE = "amd64" - $env:WIN64_ARG = "WIN64=YES" - $env:CMAKE_ARCHITECTURE = "x64" - $env:CMAKE_PARALLEL_JOBS = 4 - - $env:GitBash = "C:\Program Files\Git\bin\bash.exe" - $env:BUILD_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\build-win") - $env:PROJ_INSTALL_DIR = (Get-ForceResolvePath "$env:BUILD_ROOT\proj-build") - $env:DOWNLOADS_DIR = (Get-ForceResolvePath "$env:BUILD_ROOT\downloads") - $env:SDK_PREFIX = "$env:BUILD_ROOT\sdk\$env:SDK" - $env:GDAL_SOURCE = "$env:BUILD_ROOT\gdal-source" - $env:PROJ_SOURCE = "$env:BUILD_ROOT\proj-source" - $env:GDAL_DATA = "$env:GDAL_INSTALL_DIR\share\gdal" - $env:GDAL_DRIVER_PATH = "$env:GDAL_INSTALL_DIR\share\gdal" - $env:PROJ_LIB = "$env:PROJ_INSTALL_DIR\share\proj" - $env:GdalCmakeBuild = "$env:BUILD_ROOT\gdal-cmake-temp" - $env:SDK_LIB = "$env:SDK_PREFIX\lib" - $env:SDK_BIN = "$env:SDK_PREFIX\bin" - $env:GDAL_INSTALL_DIR = "$env:BUILD_ROOT\gdal-build" - $env:VCPKG_MANIFEST_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\shared") - $env:VCPKG_INSTALL_ROOT = "$env:BUILD_ROOT\vcpkg\installed" - $env:VCPKG_LOCKFILE = "$env:VCPKG_MANIFEST_ROOT\vcpkg-lock.json" - $env:VCPKG_INSTALLED = "$env:VCPKG_INSTALL_ROOT\x64-windows" - $env:VCPKG_INSTALLED_PKGCONFIG = "$env:VCPKG_INSTALLED\lib\pkgconfig" - - $env:WEBP_ROOT = Get-ForceResolvePath("$env:BUILD_ROOT\sdk\libwebp*") - $env:BUILD_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\build-win") - $env:7Z_ROOT = (Get-ForceResolvePath "$env:BUILD_ROOT\7z") - Add-EnvPath $env:7Z_ROOT - $env:VCPKG_ROOT_GDAL = (Get-ForceResolvePath "$env:BUILD_ROOT\vcpkg") - Add-EnvPath $env:VCPKG_ROOT_GDAL -Prepend -} - -function Get-WindowsBuildSignature { - param ( - [Parameter(Mandatory = $true)] - [string] $Component - ) - - Set-GdalVariables - - $root = Get-ForceResolvePath "$PSScriptRoot\.." - $commonInputs = Get-FileSignatures -Paths @( - "$root\shared\GdalCore.opt", - "$root\win\install.ps1", - "$root\win\partials.psm1", - "$root\win\vcpkg-makefile.vc", - "$root\shared\vcpkg.json", - "$root\shared\vcpkg-configuration.json", - "$root\shared\vcpkg-lock.json", - "$root\shared\patch\CMakeLists.txt.patch" - ) - - switch ($Component) { - "vcpkg" { - $componentData = @( - "component=vcpkg", - "vcpkgCommit=$env:VCPKG_COMMIT_VER", - "triplet=x64-windows" - ) - } - "proj" { - $componentData = @( - "component=proj", - "projVersion=$env:PROJ_VERSION", - "projSource=$(Get-GitHeadOrDefault -RepositoryPath $env:PROJ_SOURCE -DefaultValue $env:PROJ_COMMIT_VER)" - ) - } - "gdal" { - $componentData = @( - "component=gdal", - "gdalVersion=$env:GDAL_VERSION", - "gdalSource=$(Get-GitHeadOrDefault -RepositoryPath $env:GDAL_SOURCE -DefaultValue $env:GDAL_COMMIT_VER)" - ) - } - default { - throw "Unknown build component: $Component" - } - } - - return (@($componentData) + @($commonInputs)) -join "`n" -} - -function Test-WindowsBuildReuse { - param ( - [Parameter(Mandatory = $true)] - [string] $Component, - [Parameter(Mandatory = $true)] - [string[]] $RequiredPaths, - [Parameter(Mandatory = $true)] - [string] $Message - ) - - $signature = Get-WindowsBuildSignature -Component $Component - if (Test-BuildStateStamp -Component $Component -Signature $signature -RequiredPaths $RequiredPaths) { - Write-BuildStep $Message - return $true - } - - return $false -} - -function Save-WindowsBuildReuse { - param ( - [Parameter(Mandatory = $true)] - [string] $Component - ) - - Save-BuildStateStamp -Component $Component -Signature (Get-WindowsBuildSignature -Component $Component) -} - -function Get-7ZipInstallation { - Write-BuildStep "Checking for 7z installation" - New-FolderIfNotExists $env:7Z_ROOT - $env:7Z_URL = "https://www.7-zip.org/a/7z2501-extra.7z" - if (-Not (Test-Path -Path "$env:7Z_ROOT\7za.exe" -PathType Leaf)) { - Invoke-WebRequest "$env:7Z_URL" -OutFile "$env:7Z_ROOT\7z.7z" - Expand-PscxArchive -Path "$env:7Z_ROOT\7z.7z" -OutputPath "$env:7Z_ROOT\" - Write-BuildStep "Installed 7z into $env:7Z_ROOT" - } - else { - Write-BuildStep "7z is available at $env:7Z_ROOT" - } - $env:PATH += ";$env:7Z_ROOT" -} - -function Get-GdalSdkIsAvailable { - New-FolderIfNotExistsAndSetCurrentLocation $env:DOWNLOADS_DIR - Write-BuildStep "Checking for GDAL SDK from GisInternals" - if (-Not (Test-Path -Path $env:SDK_ZIP -PathType Leaf)) { - Write-BuildInfo "Downloading GDAL SDK from GisInternals" - Invoke-WebRequest "$env:SDK_URL" -OutFile "$env:SDK_ZIP" - Write-BuildStep "GDAL SDK was downloaded" - } - New-FolderIfNotExistsAndSetCurrentLocation "$env:BUILD_ROOT\sdk" - - if (-Not (Test-Path -Path "$env:BUILD_ROOT\sdk\release-*" -PathType Container)) { - exec { 7za x "$env:DOWNLOADS_DIR\$env:SDK_ZIP" -aoa } - } -} - -function Resolve-GdalThidpartyLibs { - New-FolderIfNotExistsAndSetCurrentLocation $env:DOWNLOADS_DIR - Write-BuildStep "Checking for ZSTD, Webp, Deflate libraries" - $env:LIBZSTD_ZIP = "libzstd.zip" - if (-Not (Test-Path -Path $env:LIBZSTD_ZIP -PathType Leaf)) { - Invoke-WebRequest "$env:LIBZSTD_URL" -OutFile "$env:LIBZSTD_ZIP" - } - $env:LIBWEBP_ZIP = "libwebp.zip" - if (-Not (Test-Path -Path $env:LIBWEBP_ZIP -PathType Leaf)) { - Invoke-WebRequest "$env:LIBWEBP_URL" -OutFile "$env:LIBWEBP_ZIP" - } - $env:LIBDEFLATE_ZIP = "libdeflate.zip" - if (-Not (Test-Path -Path $env:LIBDEFLATE_ZIP -PathType Leaf)) { - Invoke-WebRequest "$env:LIBDEFLATE_URL" -OutFile "$env:LIBDEFLATE_ZIP" - } - - New-FolderIfNotExistsAndSetCurrentLocation "$env:BUILD_ROOT\sdk" - exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBWEBP_ZIP" -aoa } - exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBDEFLATE_ZIP" -aoa } - exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBZSTD_ZIP" -aoa } -} -function Get-VcpkgInstallation { - param ( - [bool] $bootstrapVcpkg = $true - ) - - # Force VCPKG_ROOT to a workspace-local path to avoid VS's bundled vcpkg under Program Files - if (-not $env:VCPKG_ROOT_GDAL -or ($env:VCPKG_ROOT_GDAL -notlike "$env:BUILD_ROOT*")) { - $env:VCPKG_ROOT_GDAL = (Get-ForceResolvePath "$env:BUILD_ROOT\vcpkg") - } - - if ($null -eq $env:VCPKG_DEFAULT_BINARY_CACHE) { - $env:VCPKG_DEFAULT_BINARY_CACHE = "$env:BUILD_ROOT\vcpkg-cache" - } - New-FolderIfNotExists $env:VCPKG_DEFAULT_BINARY_CACHE - - if (Test-WindowsBuildReuse -Component "vcpkg" ` - -RequiredPaths @( - "$env:VCPKG_ROOT_GDAL\vcpkg.exe", - "$env:VCPKG_INSTALLED\include", - "$env:VCPKG_INSTALLED\lib" - ) ` - -Message "Reusing cached VCPKG installation and installed packages") { - return - } - - Write-BuildStep "Checking for VCPKG installation" - if ($bootstrapVcpkg) { - Get-CloneAndCheckoutCleanGitRepo https://github.com/Microsoft/vcpkg.git master $env:VCPKG_ROOT_GDAL - } - if (-Not (Test-Path -Path "$env:VCPKG_ROOT_GDAL/vcpkg.exe" -PathType Leaf)) { - Write-BuildInfo "Installing VCPKG" - exec { & "$env:VCPKG_ROOT_GDAL\bootstrap-vcpkg.bat" } - } - else { - Write-BuildInfo "VCPKG is already installed" - } -} - -function Install-VcpkgPackagesSharedConfig { - param ( - [bool] $installVcpkgPackages = $true - ) - - if ($installVcpkgPackages) { - if (Test-WindowsBuildReuse -Component "vcpkg" ` - -RequiredPaths @( - "$env:VCPKG_ROOT_GDAL\vcpkg.exe", - "$env:VCPKG_INSTALLED\include", - "$env:VCPKG_INSTALLED\lib" - ) ` - -Message "Skipping VCPKG manifest install because cached packages are up to date") { - return - } - - Write-BuildInfo "Installing Windows dependencies from shared manifest $env:VCPKG_MANIFEST_ROOT\vcpkg.json with lock $env:VCPKG_LOCKFILE" - exec { & nmake -f ./vcpkg-makefile.vc install_requirements } - Save-WindowsBuildReuse -Component "vcpkg" - } -} - -function Install-Proj { - param ( - [bool] $cleanProjBuild = $true, - [bool] $cleanProjIntermediate = $true - ) - - if (Test-WindowsBuildReuse -Component "proj" ` - -RequiredPaths @( - "$env:PROJ_INSTALL_DIR\include\proj.h", - "$env:PROJ_INSTALL_DIR\lib\proj.lib" - ) ` - -Message "Skipping PROJ rebuild because cached outputs are up to date") { - return - } - - Write-BuildStep "Building PROJ" - if ($cleanProjBuild) { - Write-BuildInfo "Cleaning PROJ build folder" - if (Test-Path -Path "$env:BUILD_ROOT\proj-build" -PathType Container) { - Remove-Item -Path "$env:BUILD_ROOT\proj-build" -Recurse -Force -ErrorAction SilentlyContinue - } - } - Write-BuildInfo "Checking out PROJ repo..." - - Set-Location "$PSScriptRoot" - nmake -f fetch-makefile.vc fetch-proj - - $env:ProjCmakeBuild = "$env:BUILD_ROOT\proj-cmake-temp" - if ($cleanProjIntermediate) { - Write-BuildInfo "Cleaning GDAL intermediate folder" - Remove-Item -Path $env:ProjCmakeBuild -Recurse -Force -ErrorAction SilentlyContinue - } - if ((Test-Path -Path "$env:ProjCmakeBuild\CMakeCache.txt" -PathType Leaf)) { - Write-BuildInfo "Removing build cache (CMakeCache.txt)" - Remove-Item "$env:ProjCmakeBuild\CMakeCache.txt" -ErrorAction SilentlyContinue - } - - Write-BuildInfo "Configuring PROJ..." - New-FolderIfNotExistsAndSetCurrentLocation $env:ProjCmakeBuild - - cmake -G "$env:VS_VERSION" -A $env:CMAKE_ARCHITECTURE "${env:PROJ_SOURCE}" ` - -DCMAKE_INSTALL_PREFIX="${env:PROJ_INSTALL_DIR}" ` - -DCMAKE_BUILD_TYPE=Release -Wno-dev ` - -DCMAKE_C_FLAGS="/w" ` - -DCMAKE_CXX_FLAGS="/w" ` - -DBUILD_TESTING=OFF ` - -DCMAKE_TOOLCHAIN_FILE="${env:VCPKG_ROOT_GDAL}\scripts\buildsystems\vcpkg.cmake" ` - -DCMAKE_PREFIX_PATH="${env:VCPKG_INSTALLED};${env:SDK_PREFIX}" ` - -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF ` - -DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF ` - -DBUILD_SHARED_LIBS=ON -DENABLE_CURL=ON -DENABLE_TIFF=ON ` - - exec { cmake --build . -j $env:CMAKE_PARALLEL_JOBS --config Release --target install } - Write-BuildStep "Done building PROJ" - Save-WindowsBuildReuse -Component "proj" -} - -function Get-GdalVersion { - return (Get-Content "$env:GDAL_SOURCE\VERSION") -} - -function Reset-GdalSourceBindings { - - # remove swig/csharp/[gdal|ogr|osr|const]/obj folders - $env:GdalCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\gdal\obj" - $env:OgrCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\ogr\obj" - $env:OsrCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\osr\obj" - $env:ConstCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\const\obj" - - Write-BuildInfo "Cleaning up GDAL source bindings..." - Remove-Item -Path $env:GdalCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue - Remove-Item -Path $env:OgrCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue - Remove-Item -Path $env:OsrCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue - Remove-Item -Path $env:ConstCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue - Write-BuildInfo "GDAL source bindings were cleaned up" -} - -function Build-Gdal { - param ( - [bool] $cleanGdalBuild = $true, - [bool] $cleanGdalIntermediate = $true, - [bool] $fetchGdal = $true - ) - Set-GdalVariables - $env:PATH = "$env:BUILD_ROOT\proj-build\bin;$env:SDK_BIN;$env:PATH" - $env:CMAKE_INSTALL_PREFIX = "-DCMAKE_INSTALL_PREFIX=$env:GDAL_INSTALL_DIR" - $env:PROJ_ROOT = "-DPROJ_ROOT=$env:PROJ_INSTALL_DIR" - $env:MYSQL_LIBRARY = "-DMYSQL_LIBRARY=$env:SDK_LIB\libmysql.lib" - $env:WEBP_INCLUDE = "-DWEBP_INCLUDE_DIR=$env:WEBP_ROOT\include" - $env:WEBP_LIB = "-DWEBP_LIBRARY=$env:WEBP_ROOT\lib\libwebp.lib" - - $env:Poppler_INCLUDE_DIR = "-DPoppler_INCLUDE_DIR=$env:VCPKG_INSTALLED\include\poppler" - $env:Poppler_LIBRARY = "-DPoppler_LIBRARY=$env:VCPKG_INSTALLED\lib\poppler.lib" - $env:TIFF_INCLUDE_DIR = "-DTIFF_INCLUDE_DIR=$env:VCPKG_INSTALLED\include\tiff" - $env:TIFF_LIBRARY = "-DTIFF_LIBRARY_RELEASE=$env:VCPKG_INSTALLED\lib\tiff.lib" - - # libpq must resolve from vcpkg, not from the GisInternals SDK: the SDK still - # ships PostgreSQL 14.0 (see issue #241). PostgreSQL_ROOT is searched ahead of - # CMAKE_PREFIX_PATH. Deliberately not named $env:PostgreSQL_ROOT - CMake also - # reads that as an environment variable and would pick up the "-D..." text. - # GDAL_USE_POSTGRESQL=ON below only catches "not found at all"; the packaged-DLL - # check after Get-CollectDeps is what catches "found the SDK copy instead". - $env:PG_ROOT_ARG = "-DPostgreSQL_ROOT=$env:VCPKG_INSTALLED" - - # HDF5, netCDF-C and CFITSIO must resolve from vcpkg, not from the GisInternals - # SDK (see issue #246). HDF4 (hdf.dll, mfhdf.dll, xdr.dll) intentionally stays - # from the SDK - it has no vcpkg port. - # - # hdf5 is installed WITH szip (default feature, via libaec) and the cpp feature - # (GDAL requests CXX when KEA is detected, even with GDAL_USE_KEA=OFF). On - # MSVC, libaec produces aec.dll + szip.dll; the latter replaces the SDK's - # szip.dll because VCPKG_INSTALLED\bin is searched before SDK_PREFIX\bin. This - # is safe: libaec's szip.dll is a drop-in implementing the SZ_* API that the - # SDK HDF4 (hdf.dll / mfhdf.dll) imports, matching what Debian does with - # libhdf4 + libaec. - # - # HDF5_ROOT restricts FindHDF5 to vcpkg. Without it, FindHDF5 mixes SDK - # headers (1.12.0) with vcpkg libraries (2.x) - an ABI-incompatible combination. - # HDF5_ROOT alone makes FindHDF5 find the 2.x config file and read the version, - # but HDF5 2.x's hdf5-config.cmake sets variables with a lowercase "hdf5_" - # prefix that FindHDF5 doesn't map to the expected uppercase "HDF5_INCLUDE_DIR", - # leaving HDF5_INCLUDE_DIRS empty. Pre-setting HDF5_C_INCLUDE_DIR and - # HDF5_CXX_INCLUDE_DIR as cache variables bypasses the broken extraction. - $env:HDF5_ROOT_ARG = "-DHDF5_ROOT=$env:VCPKG_INSTALLED" - $env:HDF5_C_INCLUDE_ARG = "-DHDF5_C_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" - $env:HDF5_CXX_INCLUDE_ARG = "-DHDF5_CXX_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" - $env:NETCDF_ROOT_ARG = "-DnetCDF_ROOT=$env:VCPKG_INSTALLED" - $env:CFITSIO_ROOT_ARG = "-DCFITSIO_ROOT=$env:VCPKG_INSTALLED" - - if (Test-WindowsBuildReuse -Component "gdal" ` - -RequiredPaths @( - "$env:GDAL_INSTALL_DIR\bin\gdal.dll", - "$env:GdalCmakeBuild\swig\csharp" - ) ` - -Message "Skipping GDAL rebuild because cached outputs are up to date") { - return - } - - Write-BuildStep "Configuring GDAL" - Set-Location "$env:BUILD_ROOT" - - if ($cleanGdalBuild) { - Write-BuildInfo "Cleaning GDAL build folder" - if (Test-Path -Path "$env:BUILD_ROOT\gdal-build" -PathType Container) { - Remove-Item -Path "$env:BUILD_ROOT\gdal-build" -Recurse -Force -ErrorAction SilentlyContinue - } - } - - if ($cleanGdalIntermediate) { - Write-BuildInfo "Cleaning GDAL intermediate folder" - Remove-Item -Path $env:GdalCmakeBuild -Recurse -Force -ErrorAction SilentlyContinue - } - - Set-Location "$PSScriptRoot" - - if ($fetchGdal) { - Write-BuildInfo "Fetching GDAL source" - exec { nmake -f fetch-makefile.vc fetch-gdal } - } - - if ((Test-Path -Path "$env:GdalCmakeBuild\CMakeCache.txt" -PathType Leaf)) { - Write-BuildInfo "Removing build cache (CMakeCache.txt)" - Remove-Item "$env:GdalCmakeBuild\CMakeCache.txt" -ErrorAction SilentlyContinue - } - - # PATCH 1: replace build root of SDK with our own - Set-ReplaceContentInFiles -Path $env:SDK_PREFIX ` - -FileFilter "*.pc", "*.cmake", "*.opt" ` - -What "E:/buildsystem/$env:SDK", "E:\buildsystem\$env:SDK" ` - -With $env:SDK_PREFIX.Replace("\", "/") - - Set-Location "$env:GDAL_SOURCE" - - Remove-Item -Path $env:GDAL_SOURCE/autotest -Recurse -Force -ErrorAction SilentlyContinue - # PATCH 2: apply patch to cmake pipeline. remove redundant compile steps - git apply "$PSScriptRoot\..\shared\patch\CMakeLists.txt.patch" - - New-FolderIfNotExistsAndSetCurrentLocation $env:GdalCmakeBuild - New-FolderIfNotExists "$PSScriptRoot\..\nuget" - - $env:ARCH_FLAGS = "/arch:AVX2 /Ob2 /Oi /Os /Oy /w" - # disabling KEA driver as it causes build issues on Windows - # https://github.com/OSGeo/gdal/blob/3b232ee17d8f3d93bf3535b77fbb436cb9a9c2e0/.github/workflows/windows_build.yml#L178 - - # for the same reason, we are disabling OpenEXR - # -DOpenEXR_LIBRARY="$env:VCPKG_INSTALLED\lib\OpenEXR-3_2.lib" ` - # -DOpenEXR_INCLUDE_DIR="$env:VCPKG_INSTALLED\include\OpenEXR" ` - # -DOpenEXR_UTIL_LIBRARY="$env:VCPKG_INSTALLED\lib\OpenEXRUtil-3_2.lib" ` - # -DOpenEXR_HALF_LIBRARY="$env:VCPKG_INSTALLED\lib\Imath-3_1.lib" ` - # -DOpenEXR_IEX_LIBRARY="$env:VCPKG_INSTALLED\lib\Iex-3_2.lib" ` - - cmake -G "$env:VS_VERSION" -A $env:CMAKE_ARCHITECTURE "$env:GDAL_SOURCE" ` - $env:CMAKE_INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release -Wno-dev ` - -DCMAKE_C_FLAGS="$env:ARCH_FLAGS" ` - -DCMAKE_CXX_FLAGS="$env:ARCH_FLAGS" ` - -DCMAKE_PREFIX_PATH="${env:VCPKG_INSTALLED};${env:SDK_PREFIX}" ` - -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF ` - -DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF ` - -DGDAL_USE_OPENEXR=OFF ` - $env:WEBP_INCLUDE $env:WEBP_LIB ` - $env:PROJ_ROOT $env:MYSQL_LIBRARY ` - $env:Poppler_INCLUDE_DIR $env:Poppler_LIBRARY ` - $env:PG_ROOT_ARG ` - -DGDAL_USE_POSTGRESQL=ON ` - $env:HDF5_ROOT_ARG ` - $env:HDF5_C_INCLUDE_ARG ` - $env:HDF5_CXX_INCLUDE_ARG ` - $env:NETCDF_ROOT_ARG ` - $env:CFITSIO_ROOT_ARG ` - -DGDAL_USE_HDF5=ON ` - -DGDAL_USE_NETCDF=ON ` - -DGDAL_USE_CFITSIO=ON ` - -DGDAL_USE_KEA=OFF ` - -DGDAL_USE_ZLIB_INTERNAL=ON ` - -DGDAL_CSHARP_APPS=ON ` - -DGDAL_CSHARP_TESTS=OFF ` - -DGDAL_CSHARP_BUILD_NUPKG=OFF ` - -DBUILD_CSHARP_BINDINGS=ON ` - -DBUILD_JAVA_BINDINGS=OFF ` - -DBUILD_PYTHON_BINDINGS=OFF - - # Fail early if cmake resolved any HDF5, netCDF or CFITSIO path from the SDK - # instead of vcpkg (#246). Catches mixed-ABI configurations that the DLL - # assertions alone cannot detect (e.g. SDK headers with vcpkg libraries). - $cacheFile = "$env:GdalCmakeBuild\CMakeCache.txt" - $leaked = Select-String -Path $cacheFile -Pattern "^(HDF5|netCDF|CFITSIO).*=.*$([regex]::Escape($env:SDK))" | - Where-Object { $_.Line -notmatch "^#" } - if ($leaked) { - $leaked | ForEach-Object { Write-BuildError $_.Line } - throw "CMake resolved an HDF5/netCDF/CFITSIO path from the SDK (see #246)." - } - Write-BuildStep "Verified HDF5/netCDF/CFITSIO cache entries point to vcpkg" - - Write-BuildStep "Building GDAL" - exec { cmake --build . -j $env:CMAKE_PARALLEL_JOBS --config Release --target install } - - # remove source. this was added by GDAL - exec { dotnet nuget remove source local } - Write-BuildStep "GDAL was built successfully" - Save-WindowsBuildReuse -Component "gdal" -} - -function Build-GenerateProjectFiles { - param ( - [string] $packageVersion, - [bool] $preRelease = $false - ) - Set-GdalVariables - Set-Location "$PSScriptRoot/../unix" - $vcpkgInstalled = Get-PathRelative -inputPath:$env:VCPKG_INSTALLED_PKGCONFIG -relativePath:"../build-win" - $geosVersion = (& $env:GitBash -c "make -f generate-projects-makefile get-version IN_FILE=$("$vcpkgInstalled/geos.pc")") - - $preReleaseVal = if ($preRelease) { "true" } else { "false" } - - # generate project files for C# bindings - Write-BuildStep "Generating project files for GDAL C# bindings" - exec { & $env:GitBash -c "make -f generate-projects-makefile CAT_NAME=win RUNTIME_PACKAGE_PARTIAL=WindowsRuntime TARGET_OS=win BUILD_NUMBER_TAIL=$packageVersion GEOS_VERSION=$geosVersion BUILD_ARCH=$env:CMAKE_ARCHITECTURE PRERELEASE=$preReleaseVal" } - Write-BuildStep "Done generating project files" -} - -function Build-CsharpBindings { - param ( - [bool] $isDebug = $false, - [bool] $preRelease = $false, - [string] $packageVersion - ) - Set-GdalVariables - Write-BuildStep "Building GDAL C# bindings" - - Get-VisualStudioVars - - Set-Location $PSScriptRoot - - Write-GdalFormats - - Set-Location $PSScriptRoot - - $outputPath = & nmake -f collect-deps-makefile.vc get-output - Write-BuildInfo "Output path: $outputPath" - exec { & nmake -f collect-deps-makefile.vc } - - Get-CollectDeps "$env:GDAL_INSTALL_DIR\bin\gdal.dll" "$outputPath" - - # Copy-DependentDLLs falls back to $env:SDK_PREFIX\bin, so a failed vcpkg - # resolution would silently repackage the SDK's PostgreSQL 14.0 libpq and CI - # would still pass. Refuse to ship anything but the vcpkg build (#241). - $vcpkgPq = "$env:VCPKG_INSTALLED\bin\libpq.dll" - $packagedPq = Join-Path "$outputPath" "libpq.dll" - if (-not (Test-Path $vcpkgPq) -or -not (Test-Path $packagedPq) -or - (Get-FileHash $packagedPq).Hash -ne (Get-FileHash $vcpkgPq).Hash) { - throw "Packaged libpq.dll is not the vcpkg build. Expected a copy of $vcpkgPq (see #241)." - } - Write-BuildStep "Verified packaged libpq.dll comes from vcpkg" - - # Same guard for HDF5, netCDF and CFITSIO (#246). Copy-DependentDLLs falls back - # to SDK_PREFIX\bin, which still carries the old SDK builds. - foreach ($dllName in @("hdf5.dll", "hdf5_hl.dll", "hdf5_cpp.dll", "netcdf.dll", "cfitsio.dll", "szip.dll", "aec.dll")) { - $vcpkgDll = "$env:VCPKG_INSTALLED\bin\$dllName" - $packagedDll = Join-Path "$outputPath" $dllName - if (-not (Test-Path $vcpkgDll) -or -not (Test-Path $packagedDll) -or - (Get-FileHash $packagedDll).Hash -ne (Get-FileHash $vcpkgDll).Hash) { - throw "Packaged $dllName is not the vcpkg build. Expected a copy of $vcpkgDll (see #246)." - } - } - Write-BuildStep "Verified packaged HDF5, netCDF and CFITSIO DLLs come from vcpkg" - - Build-GenerateProjectFiles -packageVersion $packageVersion -preRelease $preRelease - - Set-Location $PSScriptRoot - if ($isDebug) { - exec { & nmake -f publish-makefile.vc pack-dev DEBUG=1 PACKAGE_BUILD_NUMBER=$packageVersion } - } - else { - exec { & nmake -f publish-makefile.vc pack PACKAGE_BUILD_NUMBER=$packageVersion } - } -} - -function Convert-ToUnixPath($path) { - $unixPath = $path -replace "\\", "/" - if ($unixPath -match "^([a-zA-Z]):") { - $unixPath = $unixPath -replace "^([a-zA-Z]):", { "/$($matches[1].ToLower())" } - } - if ($unixPath -match "\s") { - $unixPath = "`"$unixPath`"" - } - return $unixPath -} - -# Function to find and copy dependent DLLs recursively -function Copy-DependentDLLs { - param ( - [string]$dllFileInternal, - [string[]]$dllDirectories, - [string]$destinationDir, - [string]$overridePath = $null # Optional parameter for path override - ) - $dllProcessed = @{} - $targetDll = [System.IO.Path]::GetFileName($dllFileInternal) - Write-BuildStep "Copying dependent DLLs for $dllFileInternal" - # Convert Windows paths to Unix paths for Git Bash and construct LD_LIBRARY_PATH - $ldLibraryPath = ($dllDirectories | ForEach-Object { Convert-ToUnixPath $_ }) -join ":" - # Use overridePath if provided, otherwise use an empty string - $combinedPath = if ($overridePath) { "${overridePath}:${ldLibraryPath}" } else { $ldLibraryPath } - - $dllFileUnix = Convert-ToUnixPath $dllFileInternal - Write-BuildInfo "DLL file unix: $dllFileUnix" - - # Get the list of dependent DLLs using ldd - Write-BuildInfo "Dry run ldd command: " - & $env:GitBash -c "PATH=${combinedPath}:`$PATH ldd $dllFileUnix" - - # Construct the LDD string - $bashCommand = if ($combinedPath) { "PATH=${combinedPath}:`$PATH ldd $dllFileUnix" } else { "ldd $dllFileUnix" } - - $lddOutput = & $env:GitBash -c "$bashCommand" - - $lddLines = $lddOutput -split "`n" - $dllPaths = @() - foreach ($line in $lddLines) { - if ($line -match "=>\s+(\/[a-z]\/.+\.dll)") { - if ($matches.Count -gt 1) { - $dllPath = $matches[1] - $dllName = [System.IO.Path]::GetFileName($dllPath) - - # we don't need system DLLs, so drop 'em if name starts with api- - if ($dllName -match "^api-") { - continue - } - # mingw64, lib, usr are also system paths, so skip them - if ($dllPath -match "^\/usr\/" -or $dllPath -match "^\/lib\/" -or $dllPath -match "^\/mingw64\/") { - continue - } - - $dllPath = $dllPath -replace "/", "\" - - if ($dllPath -match "^\\c\\Windows\\System32" -or $dllPath -match "^\\c\\Windows\\SysWOW64") { - foreach ($dir in $dllDirectories) { - $candidatePath = Join-Path -Path $dir -ChildPath $dllName - if (Test-Path -Path $candidatePath) { - Write-BuildInfo "Overriding system DLL with version from '$dir': $candidatePath" - $dllPath = $candidatePath - break - } - } - } - - # Skip system paths and include msodbcsql17.dll and other specific DLLs - $dllsToRestore = @("msodbcsql17.dll", "OpenCL.dll", "libcrypto*", "libssl*") - $shouldInclude = $false - foreach ($pattern in $dllsToRestore) { - if ($dllName -like $pattern) { - $shouldInclude = $true - break - } - } - if ($dllPath -notmatch "^\\c\\Windows" -or $shouldInclude) { - if ($dllPath -match "^\\([a-z])\\") { - $dllPath = $dllPath -replace "^\\([a-z])\\", { "$($matches[1].ToUpper()):\" } - } - Write-BuildInfo "Found candidate for copy: $dllPath" - $dllPaths += $dllPath - } - } - } - } - - $fileName = [System.IO.Path]::GetFileName($dllFileInternal) - $destinationPath = Join-Path -Path $destinationDir -ChildPath $fileName - Copy-Item -Path $dllFileInternal -Destination $destinationPath -Force - $dllProcessed[$fileName] = $true - Write-BuildInfo "Copied main DLL: $dllFileInternal => $dllPaths" - - # Copy each dependent DLL to the destination directory - foreach ($dllPathLocal in $dllPaths) { - $fileName = [System.IO.Path]::GetFileName($dllPathLocal) - $destinationPath = Join-Path -Path $destinationDir -ChildPath $fileName - - if (-Not (Test-Path -Path $destinationPath) -or (-not $dllProcessed.ContainsKey($fileName))) { - Copy-Item -Path $dllPathLocal -Destination $destinationPath -Force - Write-Output "$targetDll > Copied: $dllPathLocal to $destinationPath" - - $dllProcessed[$fileName] = $true - } - } -} - -function Get-CollectDeps { - param ( - [string] $dllFile, - [string] $destinationDir - ) - - Set-GdalVariables - $env:GDAL_DATA = "$env:GDAL_INSTALL_DIR\share\gdal" - $env:GDAL_DRIVER_PATH = "$env:GDAL_INSTALL_DIR\share\gdal" - $env:PROJ_LIB = "$env:PROJ_INSTALL_DIR\share\proj" - - $dllDirectories = @("$env:GDAL_INSTALL_DIR\bin", "$env:PROJ_INSTALL_DIR\bin", "$env:VCPKG_INSTALLED\bin", "$env:SDK_PREFIX\bin", $env:VCToolsRedistDir + "x64\") - Write-BuildInfo "Using DLL directories: $dllDirectories" - - Write-BuildInfo "Collecting dependent DLLs for $dllFile" - - Write-BuildInfo "Destination directory: $destinationDir" - - if (-Not (Test-Path -Path $destinationDir)) { - New-Item -ItemType Directory -Path $destinationDir - } - - # Start the recursive copying process - Copy-DependentDLLs -dllFileInternal $dllFile -destinationDir $destinationDir -dllDirectories $dllDirectories - - Write-BuildInfo "All dependent DLLs have been copied to $destinationDir" -} - -function Write-GdalFormats { - Set-GdalVariables - - $dllDirectories = @("$env:GDAL_INSTALL_DIR\bin", "$env:VCPKG_INSTALLED\bin", "$env:SDK_PREFIX\bin", "$env:PROJ_INSTALL_DIR\bin", "$webpRoot\bin") - $originalPath = [System.Environment]::GetEnvironmentVariable("PATH", [System.EnvironmentVariableTarget]::Process) - $newPath = ($dllDirectories -join ";") + ";" + $originalPath - [System.Environment]::SetEnvironmentVariable("PATH", $newPath, [System.EnvironmentVariableTarget]::Process) - - $formats_path = (Get-ForceResolvePath "$PSScriptRoot\..\tests\gdal-formats\formats-win") - New-FolderIfNotExists $formats_path - - Set-Location "$env:GDAL_INSTALL_DIR\bin" - try { - (& .\gdalinfo.exe --formats) | Set-Content $formats_path\gdal-formats-win-raster.txt -Force - (& .\ogrinfo.exe --formats) | Set-Content $formats_path\gdal-formats-win-vector.txt -Force - - # Fix windows style paths in gdal-config - Write-FixShellScriptOnWindows -shellScriptPath "$env:GDAL_INSTALL_DIR\bin\gdal-config" -variableName "CONFIG_DEP_LIBS" - - (& $env:GitBash -c "./gdal-config --formats") | Set-Content -Path "$formats_path\gdal-formats-win.txt" -Force - } - catch { - Write-BuildError "Failed to write GDAL formats" - Write-BuildError $_.Exception.Message - } - - # Restore the original PATH - [System.Environment]::SetEnvironmentVariable("PATH", $originalPath, [System.EnvironmentVariableTarget]::Process) -} - -function Write-FixShellScriptOnWindows { - param ( - [string] $shellScriptPath, - [string] $variableName - ) - $shellScriptContent = Get-Content -Path $shellScriptPath -Raw - $pattern = [regex]::Escape($variableName) + '="([^""].*)"' - if ($shellScriptContent -match $pattern) { - $value = $matches[1] - if ($value.Contains("\(") -or $value.Contains('\"')) { - Write-Output "Variable $variableName already fixed in the script." - return - } - $paths = $value -split ' (?=(?:[^"]*"[^"]*")*[^"]*$)' - $escapedPaths = $paths | ForEach-Object { - $_.Replace(" ", "\\ ").Replace("(", "\(").Replace(")", "\)").Replace('"', '\"'); - } - $escapedValue = ($escapedPaths -join ' ') - $newLine = "$variableName=""$escapedValue""" - $shellScriptContent = $shellScriptContent -replace "$variableName=""[^""].*""", $newLine - Set-Content -Path $shellScriptPath -Value $shellScriptContent - - Write-Output "Shell script has been updated successfully." - - } - else { - Write-Output "Variable $variableName not found in the script." - } -} +function Set-GdalVariables { + $sharedConfig = Get-ForceResolvePath "$PSScriptRoot\..\shared\GdalCore.opt" + $gdalVersion = (Select-String -Path $sharedConfig -Pattern '^GDAL_VERSION=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() + $projVersion = (Select-String -Path $sharedConfig -Pattern '^PROJ_VERSION=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() + $vcpkgCommit = (Select-String -Path $sharedConfig -Pattern '^VCPKG_COMMIT_VER=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() + + $env:GDAL_VERSION = $gdalVersion + $env:GDAL_COMMIT_VER = "v$gdalVersion" + $env:PROJ_VERSION = $projVersion + $env:PROJ_COMMIT_VER = $projVersion + $env:VCPKG_COMMIT_VER = $vcpkgCommit + + $env:VS_VERSION = "Visual Studio 17 2022" + $env:SDK = "release-1930-x64" #2022 x64 + $env:SDK_ZIP = "$env:SDK" + "-dev.zip" + $env:SDK_URL = "http://download.gisinternals.com/sdk/downloads/$env:SDK_ZIP" + $env:LIBWEBP_URL = "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-windows-x64.zip" + $env:LIBZSTD_URL = "https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-v1.5.5-win64.zip" + $env:LIBDEFLATE_URL = "https://github.com/ebiggers/libdeflate/releases/download/v1.19/libdeflate-1.19-windows-x86_64-bin.zip" + $env:VS_VER = "2022" + $env:ARCHITECTURE = "amd64" + $env:WIN64_ARG = "WIN64=YES" + $env:CMAKE_ARCHITECTURE = "x64" + $env:CMAKE_PARALLEL_JOBS = 4 + + $env:GitBash = "C:\Program Files\Git\bin\bash.exe" + $env:BUILD_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\build-win") + $env:PROJ_INSTALL_DIR = (Get-ForceResolvePath "$env:BUILD_ROOT\proj-build") + $env:DOWNLOADS_DIR = (Get-ForceResolvePath "$env:BUILD_ROOT\downloads") + $env:SDK_PREFIX = "$env:BUILD_ROOT\sdk\$env:SDK" + $env:GDAL_SOURCE = "$env:BUILD_ROOT\gdal-source" + $env:PROJ_SOURCE = "$env:BUILD_ROOT\proj-source" + $env:GDAL_DATA = "$env:GDAL_INSTALL_DIR\share\gdal" + $env:GDAL_DRIVER_PATH = "$env:GDAL_INSTALL_DIR\share\gdal" + $env:PROJ_LIB = "$env:PROJ_INSTALL_DIR\share\proj" + $env:GdalCmakeBuild = "$env:BUILD_ROOT\gdal-cmake-temp" + $env:SDK_LIB = "$env:SDK_PREFIX\lib" + $env:SDK_BIN = "$env:SDK_PREFIX\bin" + $env:GDAL_INSTALL_DIR = "$env:BUILD_ROOT\gdal-build" + $env:VCPKG_MANIFEST_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\shared") + $env:VCPKG_INSTALL_ROOT = "$env:BUILD_ROOT\vcpkg\installed" + $env:VCPKG_LOCKFILE = "$env:VCPKG_MANIFEST_ROOT\vcpkg-lock.json" + $env:VCPKG_INSTALLED = "$env:VCPKG_INSTALL_ROOT\x64-windows" + $env:VCPKG_INSTALLED_PKGCONFIG = "$env:VCPKG_INSTALLED\lib\pkgconfig" + + $env:WEBP_ROOT = Get-ForceResolvePath("$env:BUILD_ROOT\sdk\libwebp*") + $env:BUILD_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\build-win") + $env:7Z_ROOT = (Get-ForceResolvePath "$env:BUILD_ROOT\7z") + Add-EnvPath $env:7Z_ROOT + $env:VCPKG_ROOT_GDAL = (Get-ForceResolvePath "$env:BUILD_ROOT\vcpkg") + Add-EnvPath $env:VCPKG_ROOT_GDAL -Prepend +} + +function Get-WindowsBuildSignature { + param ( + [Parameter(Mandatory = $true)] + [string] $Component + ) + + Set-GdalVariables + + $root = Get-ForceResolvePath "$PSScriptRoot\.." + $commonInputs = Get-FileSignatures -Paths @( + "$root\shared\GdalCore.opt", + "$root\win\install.ps1", + "$root\win\partials.psm1", + "$root\win\vcpkg-makefile.vc", + "$root\shared\vcpkg.json", + "$root\shared\vcpkg-configuration.json", + "$root\shared\vcpkg-lock.json", + "$root\shared\patch\CMakeLists.txt.patch" + ) + + switch ($Component) { + "vcpkg" { + $componentData = @( + "component=vcpkg", + "vcpkgCommit=$env:VCPKG_COMMIT_VER", + "triplet=x64-windows" + ) + } + "proj" { + $componentData = @( + "component=proj", + "projVersion=$env:PROJ_VERSION", + "projSource=$(Get-GitHeadOrDefault -RepositoryPath $env:PROJ_SOURCE -DefaultValue $env:PROJ_COMMIT_VER)" + ) + } + "gdal" { + $componentData = @( + "component=gdal", + "gdalVersion=$env:GDAL_VERSION", + "gdalSource=$(Get-GitHeadOrDefault -RepositoryPath $env:GDAL_SOURCE -DefaultValue $env:GDAL_COMMIT_VER)" + ) + } + default { + throw "Unknown build component: $Component" + } + } + + return (@($componentData) + @($commonInputs)) -join "`n" +} + +function Test-WindowsBuildReuse { + param ( + [Parameter(Mandatory = $true)] + [string] $Component, + [Parameter(Mandatory = $true)] + [string[]] $RequiredPaths, + [Parameter(Mandatory = $true)] + [string] $Message + ) + + $signature = Get-WindowsBuildSignature -Component $Component + if (Test-BuildStateStamp -Component $Component -Signature $signature -RequiredPaths $RequiredPaths) { + Write-BuildStep $Message + return $true + } + + return $false +} + +function Save-WindowsBuildReuse { + param ( + [Parameter(Mandatory = $true)] + [string] $Component + ) + + Save-BuildStateStamp -Component $Component -Signature (Get-WindowsBuildSignature -Component $Component) +} + +function Get-7ZipInstallation { + Write-BuildStep "Checking for 7z installation" + New-FolderIfNotExists $env:7Z_ROOT + $env:7Z_URL = "https://www.7-zip.org/a/7z2501-extra.7z" + if (-Not (Test-Path -Path "$env:7Z_ROOT\7za.exe" -PathType Leaf)) { + Invoke-WebRequest "$env:7Z_URL" -OutFile "$env:7Z_ROOT\7z.7z" + Expand-PscxArchive -Path "$env:7Z_ROOT\7z.7z" -OutputPath "$env:7Z_ROOT\" + Write-BuildStep "Installed 7z into $env:7Z_ROOT" + } + else { + Write-BuildStep "7z is available at $env:7Z_ROOT" + } + $env:PATH += ";$env:7Z_ROOT" +} + +function Get-GdalSdkIsAvailable { + New-FolderIfNotExistsAndSetCurrentLocation $env:DOWNLOADS_DIR + Write-BuildStep "Checking for GDAL SDK from GisInternals" + if (-Not (Test-Path -Path $env:SDK_ZIP -PathType Leaf)) { + Write-BuildInfo "Downloading GDAL SDK from GisInternals" + Invoke-WebRequest "$env:SDK_URL" -OutFile "$env:SDK_ZIP" + Write-BuildStep "GDAL SDK was downloaded" + } + New-FolderIfNotExistsAndSetCurrentLocation "$env:BUILD_ROOT\sdk" + + if (-Not (Test-Path -Path "$env:BUILD_ROOT\sdk\release-*" -PathType Container)) { + exec { 7za x "$env:DOWNLOADS_DIR\$env:SDK_ZIP" -aoa } + } +} + +function Resolve-GdalThidpartyLibs { + New-FolderIfNotExistsAndSetCurrentLocation $env:DOWNLOADS_DIR + Write-BuildStep "Checking for ZSTD, Webp, Deflate libraries" + $env:LIBZSTD_ZIP = "libzstd.zip" + if (-Not (Test-Path -Path $env:LIBZSTD_ZIP -PathType Leaf)) { + Invoke-WebRequest "$env:LIBZSTD_URL" -OutFile "$env:LIBZSTD_ZIP" + } + $env:LIBWEBP_ZIP = "libwebp.zip" + if (-Not (Test-Path -Path $env:LIBWEBP_ZIP -PathType Leaf)) { + Invoke-WebRequest "$env:LIBWEBP_URL" -OutFile "$env:LIBWEBP_ZIP" + } + $env:LIBDEFLATE_ZIP = "libdeflate.zip" + if (-Not (Test-Path -Path $env:LIBDEFLATE_ZIP -PathType Leaf)) { + Invoke-WebRequest "$env:LIBDEFLATE_URL" -OutFile "$env:LIBDEFLATE_ZIP" + } + + New-FolderIfNotExistsAndSetCurrentLocation "$env:BUILD_ROOT\sdk" + exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBWEBP_ZIP" -aoa } + exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBDEFLATE_ZIP" -aoa } + exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBZSTD_ZIP" -aoa } +} +function Get-VcpkgInstallation { + param ( + [bool] $bootstrapVcpkg = $true + ) + + # Force VCPKG_ROOT to a workspace-local path to avoid VS's bundled vcpkg under Program Files + if (-not $env:VCPKG_ROOT_GDAL -or ($env:VCPKG_ROOT_GDAL -notlike "$env:BUILD_ROOT*")) { + $env:VCPKG_ROOT_GDAL = (Get-ForceResolvePath "$env:BUILD_ROOT\vcpkg") + } + + if ($null -eq $env:VCPKG_DEFAULT_BINARY_CACHE) { + $env:VCPKG_DEFAULT_BINARY_CACHE = "$env:BUILD_ROOT\vcpkg-cache" + } + New-FolderIfNotExists $env:VCPKG_DEFAULT_BINARY_CACHE + + if (Test-WindowsBuildReuse -Component "vcpkg" ` + -RequiredPaths @( + "$env:VCPKG_ROOT_GDAL\vcpkg.exe", + "$env:VCPKG_INSTALLED\include", + "$env:VCPKG_INSTALLED\lib" + ) ` + -Message "Reusing cached VCPKG installation and installed packages") { + return + } + + Write-BuildStep "Checking for VCPKG installation" + if ($bootstrapVcpkg) { + Get-CloneAndCheckoutCleanGitRepo https://github.com/Microsoft/vcpkg.git master $env:VCPKG_ROOT_GDAL + } + if (-Not (Test-Path -Path "$env:VCPKG_ROOT_GDAL/vcpkg.exe" -PathType Leaf)) { + Write-BuildInfo "Installing VCPKG" + exec { & "$env:VCPKG_ROOT_GDAL\bootstrap-vcpkg.bat" } + } + else { + Write-BuildInfo "VCPKG is already installed" + } +} + +function Install-VcpkgPackagesSharedConfig { + param ( + [bool] $installVcpkgPackages = $true + ) + + if ($installVcpkgPackages) { + if (Test-WindowsBuildReuse -Component "vcpkg" ` + -RequiredPaths @( + "$env:VCPKG_ROOT_GDAL\vcpkg.exe", + "$env:VCPKG_INSTALLED\include", + "$env:VCPKG_INSTALLED\lib" + ) ` + -Message "Skipping VCPKG manifest install because cached packages are up to date") { + return + } + + Write-BuildInfo "Installing Windows dependencies from shared manifest $env:VCPKG_MANIFEST_ROOT\vcpkg.json with lock $env:VCPKG_LOCKFILE" + exec { & nmake -f ./vcpkg-makefile.vc install_requirements } + Save-WindowsBuildReuse -Component "vcpkg" + } +} + +function Install-Proj { + param ( + [bool] $cleanProjBuild = $true, + [bool] $cleanProjIntermediate = $true + ) + + if (Test-WindowsBuildReuse -Component "proj" ` + -RequiredPaths @( + "$env:PROJ_INSTALL_DIR\include\proj.h", + "$env:PROJ_INSTALL_DIR\lib\proj.lib" + ) ` + -Message "Skipping PROJ rebuild because cached outputs are up to date") { + return + } + + Write-BuildStep "Building PROJ" + if ($cleanProjBuild) { + Write-BuildInfo "Cleaning PROJ build folder" + if (Test-Path -Path "$env:BUILD_ROOT\proj-build" -PathType Container) { + Remove-Item -Path "$env:BUILD_ROOT\proj-build" -Recurse -Force -ErrorAction SilentlyContinue + } + } + Write-BuildInfo "Checking out PROJ repo..." + + Set-Location "$PSScriptRoot" + nmake -f fetch-makefile.vc fetch-proj + + $env:ProjCmakeBuild = "$env:BUILD_ROOT\proj-cmake-temp" + if ($cleanProjIntermediate) { + Write-BuildInfo "Cleaning GDAL intermediate folder" + Remove-Item -Path $env:ProjCmakeBuild -Recurse -Force -ErrorAction SilentlyContinue + } + if ((Test-Path -Path "$env:ProjCmakeBuild\CMakeCache.txt" -PathType Leaf)) { + Write-BuildInfo "Removing build cache (CMakeCache.txt)" + Remove-Item "$env:ProjCmakeBuild\CMakeCache.txt" -ErrorAction SilentlyContinue + } + + Write-BuildInfo "Configuring PROJ..." + New-FolderIfNotExistsAndSetCurrentLocation $env:ProjCmakeBuild + + cmake -G "$env:VS_VERSION" -A $env:CMAKE_ARCHITECTURE "${env:PROJ_SOURCE}" ` + -DCMAKE_INSTALL_PREFIX="${env:PROJ_INSTALL_DIR}" ` + -DCMAKE_BUILD_TYPE=Release -Wno-dev ` + -DCMAKE_C_FLAGS="/w" ` + -DCMAKE_CXX_FLAGS="/w" ` + -DBUILD_TESTING=OFF ` + -DCMAKE_TOOLCHAIN_FILE="${env:VCPKG_ROOT_GDAL}\scripts\buildsystems\vcpkg.cmake" ` + -DCMAKE_PREFIX_PATH="${env:VCPKG_INSTALLED};${env:SDK_PREFIX}" ` + -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF ` + -DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF ` + -DBUILD_SHARED_LIBS=ON -DENABLE_CURL=ON -DENABLE_TIFF=ON ` + + exec { cmake --build . -j $env:CMAKE_PARALLEL_JOBS --config Release --target install } + Write-BuildStep "Done building PROJ" + Save-WindowsBuildReuse -Component "proj" +} + +function Get-GdalVersion { + return (Get-Content "$env:GDAL_SOURCE\VERSION") +} + +function Reset-GdalSourceBindings { + + # remove swig/csharp/[gdal|ogr|osr|const]/obj folders + $env:GdalCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\gdal\obj" + $env:OgrCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\ogr\obj" + $env:OsrCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\osr\obj" + $env:ConstCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\const\obj" + + Write-BuildInfo "Cleaning up GDAL source bindings..." + Remove-Item -Path $env:GdalCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path $env:OgrCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path $env:OsrCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path $env:ConstCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue + Write-BuildInfo "GDAL source bindings were cleaned up" +} + +function Build-Gdal { + param ( + [bool] $cleanGdalBuild = $true, + [bool] $cleanGdalIntermediate = $true, + [bool] $fetchGdal = $true + ) + Set-GdalVariables + $env:PATH = "$env:BUILD_ROOT\proj-build\bin;$env:SDK_BIN;$env:PATH" + $env:CMAKE_INSTALL_PREFIX = "-DCMAKE_INSTALL_PREFIX=$env:GDAL_INSTALL_DIR" + $env:PROJ_ROOT = "-DPROJ_ROOT=$env:PROJ_INSTALL_DIR" + $env:MYSQL_LIBRARY = "-DMYSQL_LIBRARY=$env:SDK_LIB\libmysql.lib" + $env:WEBP_INCLUDE = "-DWEBP_INCLUDE_DIR=$env:WEBP_ROOT\include" + $env:WEBP_LIB = "-DWEBP_LIBRARY=$env:WEBP_ROOT\lib\libwebp.lib" + + $env:Poppler_INCLUDE_DIR = "-DPoppler_INCLUDE_DIR=$env:VCPKG_INSTALLED\include\poppler" + $env:Poppler_LIBRARY = "-DPoppler_LIBRARY=$env:VCPKG_INSTALLED\lib\poppler.lib" + $env:TIFF_INCLUDE_DIR = "-DTIFF_INCLUDE_DIR=$env:VCPKG_INSTALLED\include\tiff" + $env:TIFF_LIBRARY = "-DTIFF_LIBRARY_RELEASE=$env:VCPKG_INSTALLED\lib\tiff.lib" + + # libpq must resolve from vcpkg, not from the GisInternals SDK: the SDK still + # ships PostgreSQL 14.0 (see issue #241). PostgreSQL_ROOT is searched ahead of + # CMAKE_PREFIX_PATH. Deliberately not named $env:PostgreSQL_ROOT - CMake also + # reads that as an environment variable and would pick up the "-D..." text. + # GDAL_USE_POSTGRESQL=ON below only catches "not found at all"; the packaged-DLL + # check after Get-CollectDeps is what catches "found the SDK copy instead". + $env:PG_ROOT_ARG = "-DPostgreSQL_ROOT=$env:VCPKG_INSTALLED" + + # HDF5, netCDF-C and CFITSIO must resolve from vcpkg, not from the GisInternals + # SDK (see issue #246). HDF4 (hdf.dll, mfhdf.dll, xdr.dll) intentionally stays + # from the SDK - it has no vcpkg port. + # + # hdf5 is installed WITH szip (default feature, via libaec) and the cpp feature + # (GDAL requests CXX when KEA is detected, even with GDAL_USE_KEA=OFF). On + # MSVC, libaec produces aec.dll + szip.dll; the latter replaces the SDK's + # szip.dll because VCPKG_INSTALLED\bin is searched before SDK_PREFIX\bin. This + # is safe: libaec's szip.dll is a drop-in implementing the SZ_* API that the + # SDK HDF4 (hdf.dll / mfhdf.dll) imports, matching what Debian does with + # libhdf4 + libaec. + # + # HDF5_ROOT restricts FindHDF5 to vcpkg. Without it, FindHDF5 mixes SDK + # headers (1.12.0) with vcpkg libraries (2.x) - an ABI-incompatible combination. + # HDF5_ROOT alone makes FindHDF5 find the 2.x config file and read the version, + # but HDF5 2.x's hdf5-config.cmake sets variables with a lowercase "hdf5_" + # prefix that FindHDF5 doesn't map to the expected uppercase "HDF5_INCLUDE_DIR", + # leaving HDF5_INCLUDE_DIRS empty. Pre-setting HDF5_C_INCLUDE_DIR and + # HDF5_CXX_INCLUDE_DIR as cache variables bypasses the broken extraction. + $env:HDF5_ROOT_ARG = "-DHDF5_ROOT=$env:VCPKG_INSTALLED" + $env:HDF5_C_INCLUDE_ARG = "-DHDF5_C_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" + $env:HDF5_CXX_INCLUDE_ARG = "-DHDF5_CXX_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" + $env:NETCDF_ROOT_ARG = "-DnetCDF_ROOT=$env:VCPKG_INSTALLED" + $env:CFITSIO_ROOT_ARG = "-DCFITSIO_ROOT=$env:VCPKG_INSTALLED" + + if (Test-WindowsBuildReuse -Component "gdal" ` + -RequiredPaths @( + "$env:GDAL_INSTALL_DIR\bin\gdal.dll", + "$env:GdalCmakeBuild\swig\csharp" + ) ` + -Message "Skipping GDAL rebuild because cached outputs are up to date") { + return + } + + Write-BuildStep "Configuring GDAL" + Set-Location "$env:BUILD_ROOT" + + if ($cleanGdalBuild) { + Write-BuildInfo "Cleaning GDAL build folder" + if (Test-Path -Path "$env:BUILD_ROOT\gdal-build" -PathType Container) { + Remove-Item -Path "$env:BUILD_ROOT\gdal-build" -Recurse -Force -ErrorAction SilentlyContinue + } + } + + if ($cleanGdalIntermediate) { + Write-BuildInfo "Cleaning GDAL intermediate folder" + Remove-Item -Path $env:GdalCmakeBuild -Recurse -Force -ErrorAction SilentlyContinue + } + + Set-Location "$PSScriptRoot" + + if ($fetchGdal) { + Write-BuildInfo "Fetching GDAL source" + exec { nmake -f fetch-makefile.vc fetch-gdal } + } + + if ((Test-Path -Path "$env:GdalCmakeBuild\CMakeCache.txt" -PathType Leaf)) { + Write-BuildInfo "Removing build cache (CMakeCache.txt)" + Remove-Item "$env:GdalCmakeBuild\CMakeCache.txt" -ErrorAction SilentlyContinue + } + + # PATCH 1: replace build root of SDK with our own + Set-ReplaceContentInFiles -Path $env:SDK_PREFIX ` + -FileFilter "*.pc", "*.cmake", "*.opt" ` + -What "E:/buildsystem/$env:SDK", "E:\buildsystem\$env:SDK" ` + -With $env:SDK_PREFIX.Replace("\", "/") + + Set-Location "$env:GDAL_SOURCE" + + Remove-Item -Path $env:GDAL_SOURCE/autotest -Recurse -Force -ErrorAction SilentlyContinue + # PATCH 2: apply patch to cmake pipeline. remove redundant compile steps + git apply "$PSScriptRoot\..\shared\patch\CMakeLists.txt.patch" + + New-FolderIfNotExistsAndSetCurrentLocation $env:GdalCmakeBuild + New-FolderIfNotExists "$PSScriptRoot\..\nuget" + + $env:ARCH_FLAGS = "/arch:AVX2 /Ob2 /Oi /Os /Oy /w" + # disabling KEA driver as it causes build issues on Windows + # https://github.com/OSGeo/gdal/blob/3b232ee17d8f3d93bf3535b77fbb436cb9a9c2e0/.github/workflows/windows_build.yml#L178 + + # for the same reason, we are disabling OpenEXR + # -DOpenEXR_LIBRARY="$env:VCPKG_INSTALLED\lib\OpenEXR-3_2.lib" ` + # -DOpenEXR_INCLUDE_DIR="$env:VCPKG_INSTALLED\include\OpenEXR" ` + # -DOpenEXR_UTIL_LIBRARY="$env:VCPKG_INSTALLED\lib\OpenEXRUtil-3_2.lib" ` + # -DOpenEXR_HALF_LIBRARY="$env:VCPKG_INSTALLED\lib\Imath-3_1.lib" ` + # -DOpenEXR_IEX_LIBRARY="$env:VCPKG_INSTALLED\lib\Iex-3_2.lib" ` + + cmake -G "$env:VS_VERSION" -A $env:CMAKE_ARCHITECTURE "$env:GDAL_SOURCE" ` + $env:CMAKE_INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release -Wno-dev ` + -DCMAKE_C_FLAGS="$env:ARCH_FLAGS" ` + -DCMAKE_CXX_FLAGS="$env:ARCH_FLAGS" ` + -DCMAKE_PREFIX_PATH="${env:VCPKG_INSTALLED};${env:SDK_PREFIX}" ` + -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF ` + -DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF ` + -DGDAL_USE_OPENEXR=OFF ` + $env:WEBP_INCLUDE $env:WEBP_LIB ` + $env:PROJ_ROOT $env:MYSQL_LIBRARY ` + $env:Poppler_INCLUDE_DIR $env:Poppler_LIBRARY ` + $env:PG_ROOT_ARG ` + -DGDAL_USE_POSTGRESQL=ON ` + $env:HDF5_ROOT_ARG ` + $env:HDF5_C_INCLUDE_ARG ` + $env:HDF5_CXX_INCLUDE_ARG ` + $env:NETCDF_ROOT_ARG ` + $env:CFITSIO_ROOT_ARG ` + -DGDAL_USE_HDF5=ON ` + -DGDAL_USE_NETCDF=ON ` + -DGDAL_USE_CFITSIO=ON ` + -DGDAL_USE_KEA=OFF ` + -DGDAL_USE_ZLIB_INTERNAL=ON ` + -DGDAL_CSHARP_APPS=ON ` + -DGDAL_CSHARP_TESTS=OFF ` + -DGDAL_CSHARP_BUILD_NUPKG=OFF ` + -DBUILD_CSHARP_BINDINGS=ON ` + -DBUILD_JAVA_BINDINGS=OFF ` + -DBUILD_PYTHON_BINDINGS=OFF + + # Fail early if cmake resolved any HDF5, netCDF or CFITSIO path from the SDK + # instead of vcpkg (#246). Catches mixed-ABI configurations that the DLL + # assertions alone cannot detect (e.g. SDK headers with vcpkg libraries). + $cacheFile = "$env:GdalCmakeBuild\CMakeCache.txt" + $leaked = Select-String -Path $cacheFile -Pattern "^(HDF5|netCDF|CFITSIO).*=.*$([regex]::Escape($env:SDK))" | + Where-Object { $_.Line -notmatch "^#" } + if ($leaked) { + $leaked | ForEach-Object { Write-BuildError $_.Line } + throw "CMake resolved an HDF5/netCDF/CFITSIO path from the SDK (see #246)." + } + Write-BuildStep "Verified HDF5/netCDF/CFITSIO cache entries point to vcpkg" + + Write-BuildStep "Building GDAL" + exec { cmake --build . -j $env:CMAKE_PARALLEL_JOBS --config Release --target install } + + # remove source. this was added by GDAL + exec { dotnet nuget remove source local } + Write-BuildStep "GDAL was built successfully" + Save-WindowsBuildReuse -Component "gdal" +} + +function Build-GenerateProjectFiles { + param ( + [string] $packageVersion, + [bool] $preRelease = $false + ) + Set-GdalVariables + Set-Location "$PSScriptRoot/../unix" + $vcpkgInstalled = Get-PathRelative -inputPath:$env:VCPKG_INSTALLED_PKGCONFIG -relativePath:"../build-win" + $geosVersion = (& $env:GitBash -c "make -f generate-projects-makefile get-version IN_FILE=$("$vcpkgInstalled/geos.pc")") + + $preReleaseVal = if ($preRelease) { "true" } else { "false" } + + # generate project files for C# bindings + Write-BuildStep "Generating project files for GDAL C# bindings" + exec { & $env:GitBash -c "make -f generate-projects-makefile CAT_NAME=win RUNTIME_PACKAGE_PARTIAL=WindowsRuntime TARGET_OS=win BUILD_NUMBER_TAIL=$packageVersion GEOS_VERSION=$geosVersion BUILD_ARCH=$env:CMAKE_ARCHITECTURE PRERELEASE=$preReleaseVal" } + Write-BuildStep "Done generating project files" +} + +function Build-CsharpBindings { + param ( + [bool] $isDebug = $false, + [bool] $preRelease = $false, + [string] $packageVersion + ) + Set-GdalVariables + Write-BuildStep "Building GDAL C# bindings" + + Get-VisualStudioVars + + Set-Location $PSScriptRoot + + Write-GdalFormats + + Set-Location $PSScriptRoot + + $outputPath = & nmake -f collect-deps-makefile.vc get-output + Write-BuildInfo "Output path: $outputPath" + exec { & nmake -f collect-deps-makefile.vc } + + Get-CollectDeps "$env:GDAL_INSTALL_DIR\bin\gdal.dll" "$outputPath" + + # Copy-DependentDLLs falls back to $env:SDK_PREFIX\bin, so a failed vcpkg + # resolution would silently repackage the SDK's PostgreSQL 14.0 libpq and CI + # would still pass. Refuse to ship anything but the vcpkg build (#241). + $vcpkgPq = "$env:VCPKG_INSTALLED\bin\libpq.dll" + $packagedPq = Join-Path "$outputPath" "libpq.dll" + if (-not (Test-Path $vcpkgPq) -or -not (Test-Path $packagedPq) -or + (Get-FileHash $packagedPq).Hash -ne (Get-FileHash $vcpkgPq).Hash) { + throw "Packaged libpq.dll is not the vcpkg build. Expected a copy of $vcpkgPq (see #241)." + } + Write-BuildStep "Verified packaged libpq.dll comes from vcpkg" + + # Same guard for HDF5, netCDF and CFITSIO (#246). Copy-DependentDLLs falls back + # to SDK_PREFIX\bin, which still carries the old SDK builds. + foreach ($dllName in @("hdf5.dll", "hdf5_hl.dll", "netcdf.dll", "cfitsio.dll", "szip.dll", "aec.dll")) { + $vcpkgDll = "$env:VCPKG_INSTALLED\bin\$dllName" + $packagedDll = Join-Path "$outputPath" $dllName + if (-not (Test-Path $vcpkgDll) -or -not (Test-Path $packagedDll) -or + (Get-FileHash $packagedDll).Hash -ne (Get-FileHash $vcpkgDll).Hash) { + throw "Packaged $dllName is not the vcpkg build. Expected a copy of $vcpkgDll (see #246)." + } + } + # gdal.dll links the HDF5 C++ library but imports nothing from it, so hdf5_cpp.dll + # is normally not collected. If it ever is, it must still be the vcpkg build. + $vcpkgHdf5Cpp = "$env:VCPKG_INSTALLED\bin\hdf5_cpp.dll" + $packagedHdf5Cpp = Join-Path "$outputPath" "hdf5_cpp.dll" + if ((Test-Path $packagedHdf5Cpp) -and (-not (Test-Path $vcpkgHdf5Cpp) -or + (Get-FileHash $packagedHdf5Cpp).Hash -ne (Get-FileHash $vcpkgHdf5Cpp).Hash)) { + throw "Packaged hdf5_cpp.dll is not the vcpkg build. Expected a copy of $vcpkgHdf5Cpp (see #246)." + } + Write-BuildStep "Verified packaged HDF5, netCDF and CFITSIO DLLs come from vcpkg" + + Build-GenerateProjectFiles -packageVersion $packageVersion -preRelease $preRelease + + Set-Location $PSScriptRoot + if ($isDebug) { + exec { & nmake -f publish-makefile.vc pack-dev DEBUG=1 PACKAGE_BUILD_NUMBER=$packageVersion } + } + else { + exec { & nmake -f publish-makefile.vc pack PACKAGE_BUILD_NUMBER=$packageVersion } + } +} + +function Convert-ToUnixPath($path) { + $unixPath = $path -replace "\\", "/" + if ($unixPath -match "^([a-zA-Z]):") { + $unixPath = $unixPath -replace "^([a-zA-Z]):", { "/$($matches[1].ToLower())" } + } + if ($unixPath -match "\s") { + $unixPath = "`"$unixPath`"" + } + return $unixPath +} + +# Function to find and copy dependent DLLs recursively +function Copy-DependentDLLs { + param ( + [string]$dllFileInternal, + [string[]]$dllDirectories, + [string]$destinationDir, + [string]$overridePath = $null # Optional parameter for path override + ) + $dllProcessed = @{} + $targetDll = [System.IO.Path]::GetFileName($dllFileInternal) + Write-BuildStep "Copying dependent DLLs for $dllFileInternal" + # Convert Windows paths to Unix paths for Git Bash and construct LD_LIBRARY_PATH + $ldLibraryPath = ($dllDirectories | ForEach-Object { Convert-ToUnixPath $_ }) -join ":" + # Use overridePath if provided, otherwise use an empty string + $combinedPath = if ($overridePath) { "${overridePath}:${ldLibraryPath}" } else { $ldLibraryPath } + + $dllFileUnix = Convert-ToUnixPath $dllFileInternal + Write-BuildInfo "DLL file unix: $dllFileUnix" + + # Get the list of dependent DLLs using ldd + Write-BuildInfo "Dry run ldd command: " + & $env:GitBash -c "PATH=${combinedPath}:`$PATH ldd $dllFileUnix" + + # Construct the LDD string + $bashCommand = if ($combinedPath) { "PATH=${combinedPath}:`$PATH ldd $dllFileUnix" } else { "ldd $dllFileUnix" } + + $lddOutput = & $env:GitBash -c "$bashCommand" + + $lddLines = $lddOutput -split "`n" + $dllPaths = @() + foreach ($line in $lddLines) { + if ($line -match "=>\s+(\/[a-z]\/.+\.dll)") { + if ($matches.Count -gt 1) { + $dllPath = $matches[1] + $dllName = [System.IO.Path]::GetFileName($dllPath) + + # we don't need system DLLs, so drop 'em if name starts with api- + if ($dllName -match "^api-") { + continue + } + # mingw64, lib, usr are also system paths, so skip them + if ($dllPath -match "^\/usr\/" -or $dllPath -match "^\/lib\/" -or $dllPath -match "^\/mingw64\/") { + continue + } + + $dllPath = $dllPath -replace "/", "\" + + if ($dllPath -match "^\\c\\Windows\\System32" -or $dllPath -match "^\\c\\Windows\\SysWOW64") { + foreach ($dir in $dllDirectories) { + $candidatePath = Join-Path -Path $dir -ChildPath $dllName + if (Test-Path -Path $candidatePath) { + Write-BuildInfo "Overriding system DLL with version from '$dir': $candidatePath" + $dllPath = $candidatePath + break + } + } + } + + # Skip system paths and include msodbcsql17.dll and other specific DLLs + $dllsToRestore = @("msodbcsql17.dll", "OpenCL.dll", "libcrypto*", "libssl*") + $shouldInclude = $false + foreach ($pattern in $dllsToRestore) { + if ($dllName -like $pattern) { + $shouldInclude = $true + break + } + } + if ($dllPath -notmatch "^\\c\\Windows" -or $shouldInclude) { + if ($dllPath -match "^\\([a-z])\\") { + $dllPath = $dllPath -replace "^\\([a-z])\\", { "$($matches[1].ToUpper()):\" } + } + Write-BuildInfo "Found candidate for copy: $dllPath" + $dllPaths += $dllPath + } + } + } + } + + $fileName = [System.IO.Path]::GetFileName($dllFileInternal) + $destinationPath = Join-Path -Path $destinationDir -ChildPath $fileName + Copy-Item -Path $dllFileInternal -Destination $destinationPath -Force + $dllProcessed[$fileName] = $true + Write-BuildInfo "Copied main DLL: $dllFileInternal => $dllPaths" + + # Copy each dependent DLL to the destination directory + foreach ($dllPathLocal in $dllPaths) { + $fileName = [System.IO.Path]::GetFileName($dllPathLocal) + $destinationPath = Join-Path -Path $destinationDir -ChildPath $fileName + + if (-Not (Test-Path -Path $destinationPath) -or (-not $dllProcessed.ContainsKey($fileName))) { + Copy-Item -Path $dllPathLocal -Destination $destinationPath -Force + Write-Output "$targetDll > Copied: $dllPathLocal to $destinationPath" + + $dllProcessed[$fileName] = $true + } + } +} + +function Get-CollectDeps { + param ( + [string] $dllFile, + [string] $destinationDir + ) + + Set-GdalVariables + $env:GDAL_DATA = "$env:GDAL_INSTALL_DIR\share\gdal" + $env:GDAL_DRIVER_PATH = "$env:GDAL_INSTALL_DIR\share\gdal" + $env:PROJ_LIB = "$env:PROJ_INSTALL_DIR\share\proj" + + $dllDirectories = @("$env:GDAL_INSTALL_DIR\bin", "$env:PROJ_INSTALL_DIR\bin", "$env:VCPKG_INSTALLED\bin", "$env:SDK_PREFIX\bin", $env:VCToolsRedistDir + "x64\") + Write-BuildInfo "Using DLL directories: $dllDirectories" + + Write-BuildInfo "Collecting dependent DLLs for $dllFile" + + Write-BuildInfo "Destination directory: $destinationDir" + + if (-Not (Test-Path -Path $destinationDir)) { + New-Item -ItemType Directory -Path $destinationDir + } + + # Start the recursive copying process + Copy-DependentDLLs -dllFileInternal $dllFile -destinationDir $destinationDir -dllDirectories $dllDirectories + + Write-BuildInfo "All dependent DLLs have been copied to $destinationDir" +} + +function Write-GdalFormats { + Set-GdalVariables + + $dllDirectories = @("$env:GDAL_INSTALL_DIR\bin", "$env:VCPKG_INSTALLED\bin", "$env:SDK_PREFIX\bin", "$env:PROJ_INSTALL_DIR\bin", "$webpRoot\bin") + $originalPath = [System.Environment]::GetEnvironmentVariable("PATH", [System.EnvironmentVariableTarget]::Process) + $newPath = ($dllDirectories -join ";") + ";" + $originalPath + [System.Environment]::SetEnvironmentVariable("PATH", $newPath, [System.EnvironmentVariableTarget]::Process) + + $formats_path = (Get-ForceResolvePath "$PSScriptRoot\..\tests\gdal-formats\formats-win") + New-FolderIfNotExists $formats_path + + Set-Location "$env:GDAL_INSTALL_DIR\bin" + try { + (& .\gdalinfo.exe --formats) | Set-Content $formats_path\gdal-formats-win-raster.txt -Force + (& .\ogrinfo.exe --formats) | Set-Content $formats_path\gdal-formats-win-vector.txt -Force + + # Fix windows style paths in gdal-config + Write-FixShellScriptOnWindows -shellScriptPath "$env:GDAL_INSTALL_DIR\bin\gdal-config" -variableName "CONFIG_DEP_LIBS" + + (& $env:GitBash -c "./gdal-config --formats") | Set-Content -Path "$formats_path\gdal-formats-win.txt" -Force + } + catch { + Write-BuildError "Failed to write GDAL formats" + Write-BuildError $_.Exception.Message + } + + # Restore the original PATH + [System.Environment]::SetEnvironmentVariable("PATH", $originalPath, [System.EnvironmentVariableTarget]::Process) +} + +function Write-FixShellScriptOnWindows { + param ( + [string] $shellScriptPath, + [string] $variableName + ) + $shellScriptContent = Get-Content -Path $shellScriptPath -Raw + $pattern = [regex]::Escape($variableName) + '="([^""].*)"' + if ($shellScriptContent -match $pattern) { + $value = $matches[1] + if ($value.Contains("\(") -or $value.Contains('\"')) { + Write-Output "Variable $variableName already fixed in the script." + return + } + $paths = $value -split ' (?=(?:[^"]*"[^"]*")*[^"]*$)' + $escapedPaths = $paths | ForEach-Object { + $_.Replace(" ", "\\ ").Replace("(", "\(").Replace(")", "\)").Replace('"', '\"'); + } + $escapedValue = ($escapedPaths -join ' ') + $newLine = "$variableName=""$escapedValue""" + $shellScriptContent = $shellScriptContent -replace "$variableName=""[^""].*""", $newLine + Set-Content -Path $shellScriptPath -Value $shellScriptContent + + Write-Output "Shell script has been updated successfully." + + } + else { + Write-Output "Variable $variableName not found in the script." + } +} From 00a13dc73a7c36f55e2cf142886a75c9412b14e8 Mon Sep 17 00:00:00 2001 From: "Camille-Maxime Thea (MaxRev)" Date: Fri, 25 Sep 2026 05:36:42 +0300 Subject: [PATCH 6/6] fix(win): restore LF line endings in partials.psm1 --- win/partials.psm1 | 1526 ++++++++++++++++++++++----------------------- 1 file changed, 763 insertions(+), 763 deletions(-) diff --git a/win/partials.psm1 b/win/partials.psm1 index 5f776c081..fa47f217c 100644 --- a/win/partials.psm1 +++ b/win/partials.psm1 @@ -1,763 +1,763 @@ -function Set-GdalVariables { - $sharedConfig = Get-ForceResolvePath "$PSScriptRoot\..\shared\GdalCore.opt" - $gdalVersion = (Select-String -Path $sharedConfig -Pattern '^GDAL_VERSION=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() - $projVersion = (Select-String -Path $sharedConfig -Pattern '^PROJ_VERSION=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() - $vcpkgCommit = (Select-String -Path $sharedConfig -Pattern '^VCPKG_COMMIT_VER=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() - - $env:GDAL_VERSION = $gdalVersion - $env:GDAL_COMMIT_VER = "v$gdalVersion" - $env:PROJ_VERSION = $projVersion - $env:PROJ_COMMIT_VER = $projVersion - $env:VCPKG_COMMIT_VER = $vcpkgCommit - - $env:VS_VERSION = "Visual Studio 17 2022" - $env:SDK = "release-1930-x64" #2022 x64 - $env:SDK_ZIP = "$env:SDK" + "-dev.zip" - $env:SDK_URL = "http://download.gisinternals.com/sdk/downloads/$env:SDK_ZIP" - $env:LIBWEBP_URL = "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-windows-x64.zip" - $env:LIBZSTD_URL = "https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-v1.5.5-win64.zip" - $env:LIBDEFLATE_URL = "https://github.com/ebiggers/libdeflate/releases/download/v1.19/libdeflate-1.19-windows-x86_64-bin.zip" - $env:VS_VER = "2022" - $env:ARCHITECTURE = "amd64" - $env:WIN64_ARG = "WIN64=YES" - $env:CMAKE_ARCHITECTURE = "x64" - $env:CMAKE_PARALLEL_JOBS = 4 - - $env:GitBash = "C:\Program Files\Git\bin\bash.exe" - $env:BUILD_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\build-win") - $env:PROJ_INSTALL_DIR = (Get-ForceResolvePath "$env:BUILD_ROOT\proj-build") - $env:DOWNLOADS_DIR = (Get-ForceResolvePath "$env:BUILD_ROOT\downloads") - $env:SDK_PREFIX = "$env:BUILD_ROOT\sdk\$env:SDK" - $env:GDAL_SOURCE = "$env:BUILD_ROOT\gdal-source" - $env:PROJ_SOURCE = "$env:BUILD_ROOT\proj-source" - $env:GDAL_DATA = "$env:GDAL_INSTALL_DIR\share\gdal" - $env:GDAL_DRIVER_PATH = "$env:GDAL_INSTALL_DIR\share\gdal" - $env:PROJ_LIB = "$env:PROJ_INSTALL_DIR\share\proj" - $env:GdalCmakeBuild = "$env:BUILD_ROOT\gdal-cmake-temp" - $env:SDK_LIB = "$env:SDK_PREFIX\lib" - $env:SDK_BIN = "$env:SDK_PREFIX\bin" - $env:GDAL_INSTALL_DIR = "$env:BUILD_ROOT\gdal-build" - $env:VCPKG_MANIFEST_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\shared") - $env:VCPKG_INSTALL_ROOT = "$env:BUILD_ROOT\vcpkg\installed" - $env:VCPKG_LOCKFILE = "$env:VCPKG_MANIFEST_ROOT\vcpkg-lock.json" - $env:VCPKG_INSTALLED = "$env:VCPKG_INSTALL_ROOT\x64-windows" - $env:VCPKG_INSTALLED_PKGCONFIG = "$env:VCPKG_INSTALLED\lib\pkgconfig" - - $env:WEBP_ROOT = Get-ForceResolvePath("$env:BUILD_ROOT\sdk\libwebp*") - $env:BUILD_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\build-win") - $env:7Z_ROOT = (Get-ForceResolvePath "$env:BUILD_ROOT\7z") - Add-EnvPath $env:7Z_ROOT - $env:VCPKG_ROOT_GDAL = (Get-ForceResolvePath "$env:BUILD_ROOT\vcpkg") - Add-EnvPath $env:VCPKG_ROOT_GDAL -Prepend -} - -function Get-WindowsBuildSignature { - param ( - [Parameter(Mandatory = $true)] - [string] $Component - ) - - Set-GdalVariables - - $root = Get-ForceResolvePath "$PSScriptRoot\.." - $commonInputs = Get-FileSignatures -Paths @( - "$root\shared\GdalCore.opt", - "$root\win\install.ps1", - "$root\win\partials.psm1", - "$root\win\vcpkg-makefile.vc", - "$root\shared\vcpkg.json", - "$root\shared\vcpkg-configuration.json", - "$root\shared\vcpkg-lock.json", - "$root\shared\patch\CMakeLists.txt.patch" - ) - - switch ($Component) { - "vcpkg" { - $componentData = @( - "component=vcpkg", - "vcpkgCommit=$env:VCPKG_COMMIT_VER", - "triplet=x64-windows" - ) - } - "proj" { - $componentData = @( - "component=proj", - "projVersion=$env:PROJ_VERSION", - "projSource=$(Get-GitHeadOrDefault -RepositoryPath $env:PROJ_SOURCE -DefaultValue $env:PROJ_COMMIT_VER)" - ) - } - "gdal" { - $componentData = @( - "component=gdal", - "gdalVersion=$env:GDAL_VERSION", - "gdalSource=$(Get-GitHeadOrDefault -RepositoryPath $env:GDAL_SOURCE -DefaultValue $env:GDAL_COMMIT_VER)" - ) - } - default { - throw "Unknown build component: $Component" - } - } - - return (@($componentData) + @($commonInputs)) -join "`n" -} - -function Test-WindowsBuildReuse { - param ( - [Parameter(Mandatory = $true)] - [string] $Component, - [Parameter(Mandatory = $true)] - [string[]] $RequiredPaths, - [Parameter(Mandatory = $true)] - [string] $Message - ) - - $signature = Get-WindowsBuildSignature -Component $Component - if (Test-BuildStateStamp -Component $Component -Signature $signature -RequiredPaths $RequiredPaths) { - Write-BuildStep $Message - return $true - } - - return $false -} - -function Save-WindowsBuildReuse { - param ( - [Parameter(Mandatory = $true)] - [string] $Component - ) - - Save-BuildStateStamp -Component $Component -Signature (Get-WindowsBuildSignature -Component $Component) -} - -function Get-7ZipInstallation { - Write-BuildStep "Checking for 7z installation" - New-FolderIfNotExists $env:7Z_ROOT - $env:7Z_URL = "https://www.7-zip.org/a/7z2501-extra.7z" - if (-Not (Test-Path -Path "$env:7Z_ROOT\7za.exe" -PathType Leaf)) { - Invoke-WebRequest "$env:7Z_URL" -OutFile "$env:7Z_ROOT\7z.7z" - Expand-PscxArchive -Path "$env:7Z_ROOT\7z.7z" -OutputPath "$env:7Z_ROOT\" - Write-BuildStep "Installed 7z into $env:7Z_ROOT" - } - else { - Write-BuildStep "7z is available at $env:7Z_ROOT" - } - $env:PATH += ";$env:7Z_ROOT" -} - -function Get-GdalSdkIsAvailable { - New-FolderIfNotExistsAndSetCurrentLocation $env:DOWNLOADS_DIR - Write-BuildStep "Checking for GDAL SDK from GisInternals" - if (-Not (Test-Path -Path $env:SDK_ZIP -PathType Leaf)) { - Write-BuildInfo "Downloading GDAL SDK from GisInternals" - Invoke-WebRequest "$env:SDK_URL" -OutFile "$env:SDK_ZIP" - Write-BuildStep "GDAL SDK was downloaded" - } - New-FolderIfNotExistsAndSetCurrentLocation "$env:BUILD_ROOT\sdk" - - if (-Not (Test-Path -Path "$env:BUILD_ROOT\sdk\release-*" -PathType Container)) { - exec { 7za x "$env:DOWNLOADS_DIR\$env:SDK_ZIP" -aoa } - } -} - -function Resolve-GdalThidpartyLibs { - New-FolderIfNotExistsAndSetCurrentLocation $env:DOWNLOADS_DIR - Write-BuildStep "Checking for ZSTD, Webp, Deflate libraries" - $env:LIBZSTD_ZIP = "libzstd.zip" - if (-Not (Test-Path -Path $env:LIBZSTD_ZIP -PathType Leaf)) { - Invoke-WebRequest "$env:LIBZSTD_URL" -OutFile "$env:LIBZSTD_ZIP" - } - $env:LIBWEBP_ZIP = "libwebp.zip" - if (-Not (Test-Path -Path $env:LIBWEBP_ZIP -PathType Leaf)) { - Invoke-WebRequest "$env:LIBWEBP_URL" -OutFile "$env:LIBWEBP_ZIP" - } - $env:LIBDEFLATE_ZIP = "libdeflate.zip" - if (-Not (Test-Path -Path $env:LIBDEFLATE_ZIP -PathType Leaf)) { - Invoke-WebRequest "$env:LIBDEFLATE_URL" -OutFile "$env:LIBDEFLATE_ZIP" - } - - New-FolderIfNotExistsAndSetCurrentLocation "$env:BUILD_ROOT\sdk" - exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBWEBP_ZIP" -aoa } - exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBDEFLATE_ZIP" -aoa } - exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBZSTD_ZIP" -aoa } -} -function Get-VcpkgInstallation { - param ( - [bool] $bootstrapVcpkg = $true - ) - - # Force VCPKG_ROOT to a workspace-local path to avoid VS's bundled vcpkg under Program Files - if (-not $env:VCPKG_ROOT_GDAL -or ($env:VCPKG_ROOT_GDAL -notlike "$env:BUILD_ROOT*")) { - $env:VCPKG_ROOT_GDAL = (Get-ForceResolvePath "$env:BUILD_ROOT\vcpkg") - } - - if ($null -eq $env:VCPKG_DEFAULT_BINARY_CACHE) { - $env:VCPKG_DEFAULT_BINARY_CACHE = "$env:BUILD_ROOT\vcpkg-cache" - } - New-FolderIfNotExists $env:VCPKG_DEFAULT_BINARY_CACHE - - if (Test-WindowsBuildReuse -Component "vcpkg" ` - -RequiredPaths @( - "$env:VCPKG_ROOT_GDAL\vcpkg.exe", - "$env:VCPKG_INSTALLED\include", - "$env:VCPKG_INSTALLED\lib" - ) ` - -Message "Reusing cached VCPKG installation and installed packages") { - return - } - - Write-BuildStep "Checking for VCPKG installation" - if ($bootstrapVcpkg) { - Get-CloneAndCheckoutCleanGitRepo https://github.com/Microsoft/vcpkg.git master $env:VCPKG_ROOT_GDAL - } - if (-Not (Test-Path -Path "$env:VCPKG_ROOT_GDAL/vcpkg.exe" -PathType Leaf)) { - Write-BuildInfo "Installing VCPKG" - exec { & "$env:VCPKG_ROOT_GDAL\bootstrap-vcpkg.bat" } - } - else { - Write-BuildInfo "VCPKG is already installed" - } -} - -function Install-VcpkgPackagesSharedConfig { - param ( - [bool] $installVcpkgPackages = $true - ) - - if ($installVcpkgPackages) { - if (Test-WindowsBuildReuse -Component "vcpkg" ` - -RequiredPaths @( - "$env:VCPKG_ROOT_GDAL\vcpkg.exe", - "$env:VCPKG_INSTALLED\include", - "$env:VCPKG_INSTALLED\lib" - ) ` - -Message "Skipping VCPKG manifest install because cached packages are up to date") { - return - } - - Write-BuildInfo "Installing Windows dependencies from shared manifest $env:VCPKG_MANIFEST_ROOT\vcpkg.json with lock $env:VCPKG_LOCKFILE" - exec { & nmake -f ./vcpkg-makefile.vc install_requirements } - Save-WindowsBuildReuse -Component "vcpkg" - } -} - -function Install-Proj { - param ( - [bool] $cleanProjBuild = $true, - [bool] $cleanProjIntermediate = $true - ) - - if (Test-WindowsBuildReuse -Component "proj" ` - -RequiredPaths @( - "$env:PROJ_INSTALL_DIR\include\proj.h", - "$env:PROJ_INSTALL_DIR\lib\proj.lib" - ) ` - -Message "Skipping PROJ rebuild because cached outputs are up to date") { - return - } - - Write-BuildStep "Building PROJ" - if ($cleanProjBuild) { - Write-BuildInfo "Cleaning PROJ build folder" - if (Test-Path -Path "$env:BUILD_ROOT\proj-build" -PathType Container) { - Remove-Item -Path "$env:BUILD_ROOT\proj-build" -Recurse -Force -ErrorAction SilentlyContinue - } - } - Write-BuildInfo "Checking out PROJ repo..." - - Set-Location "$PSScriptRoot" - nmake -f fetch-makefile.vc fetch-proj - - $env:ProjCmakeBuild = "$env:BUILD_ROOT\proj-cmake-temp" - if ($cleanProjIntermediate) { - Write-BuildInfo "Cleaning GDAL intermediate folder" - Remove-Item -Path $env:ProjCmakeBuild -Recurse -Force -ErrorAction SilentlyContinue - } - if ((Test-Path -Path "$env:ProjCmakeBuild\CMakeCache.txt" -PathType Leaf)) { - Write-BuildInfo "Removing build cache (CMakeCache.txt)" - Remove-Item "$env:ProjCmakeBuild\CMakeCache.txt" -ErrorAction SilentlyContinue - } - - Write-BuildInfo "Configuring PROJ..." - New-FolderIfNotExistsAndSetCurrentLocation $env:ProjCmakeBuild - - cmake -G "$env:VS_VERSION" -A $env:CMAKE_ARCHITECTURE "${env:PROJ_SOURCE}" ` - -DCMAKE_INSTALL_PREFIX="${env:PROJ_INSTALL_DIR}" ` - -DCMAKE_BUILD_TYPE=Release -Wno-dev ` - -DCMAKE_C_FLAGS="/w" ` - -DCMAKE_CXX_FLAGS="/w" ` - -DBUILD_TESTING=OFF ` - -DCMAKE_TOOLCHAIN_FILE="${env:VCPKG_ROOT_GDAL}\scripts\buildsystems\vcpkg.cmake" ` - -DCMAKE_PREFIX_PATH="${env:VCPKG_INSTALLED};${env:SDK_PREFIX}" ` - -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF ` - -DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF ` - -DBUILD_SHARED_LIBS=ON -DENABLE_CURL=ON -DENABLE_TIFF=ON ` - - exec { cmake --build . -j $env:CMAKE_PARALLEL_JOBS --config Release --target install } - Write-BuildStep "Done building PROJ" - Save-WindowsBuildReuse -Component "proj" -} - -function Get-GdalVersion { - return (Get-Content "$env:GDAL_SOURCE\VERSION") -} - -function Reset-GdalSourceBindings { - - # remove swig/csharp/[gdal|ogr|osr|const]/obj folders - $env:GdalCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\gdal\obj" - $env:OgrCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\ogr\obj" - $env:OsrCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\osr\obj" - $env:ConstCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\const\obj" - - Write-BuildInfo "Cleaning up GDAL source bindings..." - Remove-Item -Path $env:GdalCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue - Remove-Item -Path $env:OgrCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue - Remove-Item -Path $env:OsrCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue - Remove-Item -Path $env:ConstCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue - Write-BuildInfo "GDAL source bindings were cleaned up" -} - -function Build-Gdal { - param ( - [bool] $cleanGdalBuild = $true, - [bool] $cleanGdalIntermediate = $true, - [bool] $fetchGdal = $true - ) - Set-GdalVariables - $env:PATH = "$env:BUILD_ROOT\proj-build\bin;$env:SDK_BIN;$env:PATH" - $env:CMAKE_INSTALL_PREFIX = "-DCMAKE_INSTALL_PREFIX=$env:GDAL_INSTALL_DIR" - $env:PROJ_ROOT = "-DPROJ_ROOT=$env:PROJ_INSTALL_DIR" - $env:MYSQL_LIBRARY = "-DMYSQL_LIBRARY=$env:SDK_LIB\libmysql.lib" - $env:WEBP_INCLUDE = "-DWEBP_INCLUDE_DIR=$env:WEBP_ROOT\include" - $env:WEBP_LIB = "-DWEBP_LIBRARY=$env:WEBP_ROOT\lib\libwebp.lib" - - $env:Poppler_INCLUDE_DIR = "-DPoppler_INCLUDE_DIR=$env:VCPKG_INSTALLED\include\poppler" - $env:Poppler_LIBRARY = "-DPoppler_LIBRARY=$env:VCPKG_INSTALLED\lib\poppler.lib" - $env:TIFF_INCLUDE_DIR = "-DTIFF_INCLUDE_DIR=$env:VCPKG_INSTALLED\include\tiff" - $env:TIFF_LIBRARY = "-DTIFF_LIBRARY_RELEASE=$env:VCPKG_INSTALLED\lib\tiff.lib" - - # libpq must resolve from vcpkg, not from the GisInternals SDK: the SDK still - # ships PostgreSQL 14.0 (see issue #241). PostgreSQL_ROOT is searched ahead of - # CMAKE_PREFIX_PATH. Deliberately not named $env:PostgreSQL_ROOT - CMake also - # reads that as an environment variable and would pick up the "-D..." text. - # GDAL_USE_POSTGRESQL=ON below only catches "not found at all"; the packaged-DLL - # check after Get-CollectDeps is what catches "found the SDK copy instead". - $env:PG_ROOT_ARG = "-DPostgreSQL_ROOT=$env:VCPKG_INSTALLED" - - # HDF5, netCDF-C and CFITSIO must resolve from vcpkg, not from the GisInternals - # SDK (see issue #246). HDF4 (hdf.dll, mfhdf.dll, xdr.dll) intentionally stays - # from the SDK - it has no vcpkg port. - # - # hdf5 is installed WITH szip (default feature, via libaec) and the cpp feature - # (GDAL requests CXX when KEA is detected, even with GDAL_USE_KEA=OFF). On - # MSVC, libaec produces aec.dll + szip.dll; the latter replaces the SDK's - # szip.dll because VCPKG_INSTALLED\bin is searched before SDK_PREFIX\bin. This - # is safe: libaec's szip.dll is a drop-in implementing the SZ_* API that the - # SDK HDF4 (hdf.dll / mfhdf.dll) imports, matching what Debian does with - # libhdf4 + libaec. - # - # HDF5_ROOT restricts FindHDF5 to vcpkg. Without it, FindHDF5 mixes SDK - # headers (1.12.0) with vcpkg libraries (2.x) - an ABI-incompatible combination. - # HDF5_ROOT alone makes FindHDF5 find the 2.x config file and read the version, - # but HDF5 2.x's hdf5-config.cmake sets variables with a lowercase "hdf5_" - # prefix that FindHDF5 doesn't map to the expected uppercase "HDF5_INCLUDE_DIR", - # leaving HDF5_INCLUDE_DIRS empty. Pre-setting HDF5_C_INCLUDE_DIR and - # HDF5_CXX_INCLUDE_DIR as cache variables bypasses the broken extraction. - $env:HDF5_ROOT_ARG = "-DHDF5_ROOT=$env:VCPKG_INSTALLED" - $env:HDF5_C_INCLUDE_ARG = "-DHDF5_C_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" - $env:HDF5_CXX_INCLUDE_ARG = "-DHDF5_CXX_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" - $env:NETCDF_ROOT_ARG = "-DnetCDF_ROOT=$env:VCPKG_INSTALLED" - $env:CFITSIO_ROOT_ARG = "-DCFITSIO_ROOT=$env:VCPKG_INSTALLED" - - if (Test-WindowsBuildReuse -Component "gdal" ` - -RequiredPaths @( - "$env:GDAL_INSTALL_DIR\bin\gdal.dll", - "$env:GdalCmakeBuild\swig\csharp" - ) ` - -Message "Skipping GDAL rebuild because cached outputs are up to date") { - return - } - - Write-BuildStep "Configuring GDAL" - Set-Location "$env:BUILD_ROOT" - - if ($cleanGdalBuild) { - Write-BuildInfo "Cleaning GDAL build folder" - if (Test-Path -Path "$env:BUILD_ROOT\gdal-build" -PathType Container) { - Remove-Item -Path "$env:BUILD_ROOT\gdal-build" -Recurse -Force -ErrorAction SilentlyContinue - } - } - - if ($cleanGdalIntermediate) { - Write-BuildInfo "Cleaning GDAL intermediate folder" - Remove-Item -Path $env:GdalCmakeBuild -Recurse -Force -ErrorAction SilentlyContinue - } - - Set-Location "$PSScriptRoot" - - if ($fetchGdal) { - Write-BuildInfo "Fetching GDAL source" - exec { nmake -f fetch-makefile.vc fetch-gdal } - } - - if ((Test-Path -Path "$env:GdalCmakeBuild\CMakeCache.txt" -PathType Leaf)) { - Write-BuildInfo "Removing build cache (CMakeCache.txt)" - Remove-Item "$env:GdalCmakeBuild\CMakeCache.txt" -ErrorAction SilentlyContinue - } - - # PATCH 1: replace build root of SDK with our own - Set-ReplaceContentInFiles -Path $env:SDK_PREFIX ` - -FileFilter "*.pc", "*.cmake", "*.opt" ` - -What "E:/buildsystem/$env:SDK", "E:\buildsystem\$env:SDK" ` - -With $env:SDK_PREFIX.Replace("\", "/") - - Set-Location "$env:GDAL_SOURCE" - - Remove-Item -Path $env:GDAL_SOURCE/autotest -Recurse -Force -ErrorAction SilentlyContinue - # PATCH 2: apply patch to cmake pipeline. remove redundant compile steps - git apply "$PSScriptRoot\..\shared\patch\CMakeLists.txt.patch" - - New-FolderIfNotExistsAndSetCurrentLocation $env:GdalCmakeBuild - New-FolderIfNotExists "$PSScriptRoot\..\nuget" - - $env:ARCH_FLAGS = "/arch:AVX2 /Ob2 /Oi /Os /Oy /w" - # disabling KEA driver as it causes build issues on Windows - # https://github.com/OSGeo/gdal/blob/3b232ee17d8f3d93bf3535b77fbb436cb9a9c2e0/.github/workflows/windows_build.yml#L178 - - # for the same reason, we are disabling OpenEXR - # -DOpenEXR_LIBRARY="$env:VCPKG_INSTALLED\lib\OpenEXR-3_2.lib" ` - # -DOpenEXR_INCLUDE_DIR="$env:VCPKG_INSTALLED\include\OpenEXR" ` - # -DOpenEXR_UTIL_LIBRARY="$env:VCPKG_INSTALLED\lib\OpenEXRUtil-3_2.lib" ` - # -DOpenEXR_HALF_LIBRARY="$env:VCPKG_INSTALLED\lib\Imath-3_1.lib" ` - # -DOpenEXR_IEX_LIBRARY="$env:VCPKG_INSTALLED\lib\Iex-3_2.lib" ` - - cmake -G "$env:VS_VERSION" -A $env:CMAKE_ARCHITECTURE "$env:GDAL_SOURCE" ` - $env:CMAKE_INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release -Wno-dev ` - -DCMAKE_C_FLAGS="$env:ARCH_FLAGS" ` - -DCMAKE_CXX_FLAGS="$env:ARCH_FLAGS" ` - -DCMAKE_PREFIX_PATH="${env:VCPKG_INSTALLED};${env:SDK_PREFIX}" ` - -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF ` - -DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF ` - -DGDAL_USE_OPENEXR=OFF ` - $env:WEBP_INCLUDE $env:WEBP_LIB ` - $env:PROJ_ROOT $env:MYSQL_LIBRARY ` - $env:Poppler_INCLUDE_DIR $env:Poppler_LIBRARY ` - $env:PG_ROOT_ARG ` - -DGDAL_USE_POSTGRESQL=ON ` - $env:HDF5_ROOT_ARG ` - $env:HDF5_C_INCLUDE_ARG ` - $env:HDF5_CXX_INCLUDE_ARG ` - $env:NETCDF_ROOT_ARG ` - $env:CFITSIO_ROOT_ARG ` - -DGDAL_USE_HDF5=ON ` - -DGDAL_USE_NETCDF=ON ` - -DGDAL_USE_CFITSIO=ON ` - -DGDAL_USE_KEA=OFF ` - -DGDAL_USE_ZLIB_INTERNAL=ON ` - -DGDAL_CSHARP_APPS=ON ` - -DGDAL_CSHARP_TESTS=OFF ` - -DGDAL_CSHARP_BUILD_NUPKG=OFF ` - -DBUILD_CSHARP_BINDINGS=ON ` - -DBUILD_JAVA_BINDINGS=OFF ` - -DBUILD_PYTHON_BINDINGS=OFF - - # Fail early if cmake resolved any HDF5, netCDF or CFITSIO path from the SDK - # instead of vcpkg (#246). Catches mixed-ABI configurations that the DLL - # assertions alone cannot detect (e.g. SDK headers with vcpkg libraries). - $cacheFile = "$env:GdalCmakeBuild\CMakeCache.txt" - $leaked = Select-String -Path $cacheFile -Pattern "^(HDF5|netCDF|CFITSIO).*=.*$([regex]::Escape($env:SDK))" | - Where-Object { $_.Line -notmatch "^#" } - if ($leaked) { - $leaked | ForEach-Object { Write-BuildError $_.Line } - throw "CMake resolved an HDF5/netCDF/CFITSIO path from the SDK (see #246)." - } - Write-BuildStep "Verified HDF5/netCDF/CFITSIO cache entries point to vcpkg" - - Write-BuildStep "Building GDAL" - exec { cmake --build . -j $env:CMAKE_PARALLEL_JOBS --config Release --target install } - - # remove source. this was added by GDAL - exec { dotnet nuget remove source local } - Write-BuildStep "GDAL was built successfully" - Save-WindowsBuildReuse -Component "gdal" -} - -function Build-GenerateProjectFiles { - param ( - [string] $packageVersion, - [bool] $preRelease = $false - ) - Set-GdalVariables - Set-Location "$PSScriptRoot/../unix" - $vcpkgInstalled = Get-PathRelative -inputPath:$env:VCPKG_INSTALLED_PKGCONFIG -relativePath:"../build-win" - $geosVersion = (& $env:GitBash -c "make -f generate-projects-makefile get-version IN_FILE=$("$vcpkgInstalled/geos.pc")") - - $preReleaseVal = if ($preRelease) { "true" } else { "false" } - - # generate project files for C# bindings - Write-BuildStep "Generating project files for GDAL C# bindings" - exec { & $env:GitBash -c "make -f generate-projects-makefile CAT_NAME=win RUNTIME_PACKAGE_PARTIAL=WindowsRuntime TARGET_OS=win BUILD_NUMBER_TAIL=$packageVersion GEOS_VERSION=$geosVersion BUILD_ARCH=$env:CMAKE_ARCHITECTURE PRERELEASE=$preReleaseVal" } - Write-BuildStep "Done generating project files" -} - -function Build-CsharpBindings { - param ( - [bool] $isDebug = $false, - [bool] $preRelease = $false, - [string] $packageVersion - ) - Set-GdalVariables - Write-BuildStep "Building GDAL C# bindings" - - Get-VisualStudioVars - - Set-Location $PSScriptRoot - - Write-GdalFormats - - Set-Location $PSScriptRoot - - $outputPath = & nmake -f collect-deps-makefile.vc get-output - Write-BuildInfo "Output path: $outputPath" - exec { & nmake -f collect-deps-makefile.vc } - - Get-CollectDeps "$env:GDAL_INSTALL_DIR\bin\gdal.dll" "$outputPath" - - # Copy-DependentDLLs falls back to $env:SDK_PREFIX\bin, so a failed vcpkg - # resolution would silently repackage the SDK's PostgreSQL 14.0 libpq and CI - # would still pass. Refuse to ship anything but the vcpkg build (#241). - $vcpkgPq = "$env:VCPKG_INSTALLED\bin\libpq.dll" - $packagedPq = Join-Path "$outputPath" "libpq.dll" - if (-not (Test-Path $vcpkgPq) -or -not (Test-Path $packagedPq) -or - (Get-FileHash $packagedPq).Hash -ne (Get-FileHash $vcpkgPq).Hash) { - throw "Packaged libpq.dll is not the vcpkg build. Expected a copy of $vcpkgPq (see #241)." - } - Write-BuildStep "Verified packaged libpq.dll comes from vcpkg" - - # Same guard for HDF5, netCDF and CFITSIO (#246). Copy-DependentDLLs falls back - # to SDK_PREFIX\bin, which still carries the old SDK builds. - foreach ($dllName in @("hdf5.dll", "hdf5_hl.dll", "netcdf.dll", "cfitsio.dll", "szip.dll", "aec.dll")) { - $vcpkgDll = "$env:VCPKG_INSTALLED\bin\$dllName" - $packagedDll = Join-Path "$outputPath" $dllName - if (-not (Test-Path $vcpkgDll) -or -not (Test-Path $packagedDll) -or - (Get-FileHash $packagedDll).Hash -ne (Get-FileHash $vcpkgDll).Hash) { - throw "Packaged $dllName is not the vcpkg build. Expected a copy of $vcpkgDll (see #246)." - } - } - # gdal.dll links the HDF5 C++ library but imports nothing from it, so hdf5_cpp.dll - # is normally not collected. If it ever is, it must still be the vcpkg build. - $vcpkgHdf5Cpp = "$env:VCPKG_INSTALLED\bin\hdf5_cpp.dll" - $packagedHdf5Cpp = Join-Path "$outputPath" "hdf5_cpp.dll" - if ((Test-Path $packagedHdf5Cpp) -and (-not (Test-Path $vcpkgHdf5Cpp) -or - (Get-FileHash $packagedHdf5Cpp).Hash -ne (Get-FileHash $vcpkgHdf5Cpp).Hash)) { - throw "Packaged hdf5_cpp.dll is not the vcpkg build. Expected a copy of $vcpkgHdf5Cpp (see #246)." - } - Write-BuildStep "Verified packaged HDF5, netCDF and CFITSIO DLLs come from vcpkg" - - Build-GenerateProjectFiles -packageVersion $packageVersion -preRelease $preRelease - - Set-Location $PSScriptRoot - if ($isDebug) { - exec { & nmake -f publish-makefile.vc pack-dev DEBUG=1 PACKAGE_BUILD_NUMBER=$packageVersion } - } - else { - exec { & nmake -f publish-makefile.vc pack PACKAGE_BUILD_NUMBER=$packageVersion } - } -} - -function Convert-ToUnixPath($path) { - $unixPath = $path -replace "\\", "/" - if ($unixPath -match "^([a-zA-Z]):") { - $unixPath = $unixPath -replace "^([a-zA-Z]):", { "/$($matches[1].ToLower())" } - } - if ($unixPath -match "\s") { - $unixPath = "`"$unixPath`"" - } - return $unixPath -} - -# Function to find and copy dependent DLLs recursively -function Copy-DependentDLLs { - param ( - [string]$dllFileInternal, - [string[]]$dllDirectories, - [string]$destinationDir, - [string]$overridePath = $null # Optional parameter for path override - ) - $dllProcessed = @{} - $targetDll = [System.IO.Path]::GetFileName($dllFileInternal) - Write-BuildStep "Copying dependent DLLs for $dllFileInternal" - # Convert Windows paths to Unix paths for Git Bash and construct LD_LIBRARY_PATH - $ldLibraryPath = ($dllDirectories | ForEach-Object { Convert-ToUnixPath $_ }) -join ":" - # Use overridePath if provided, otherwise use an empty string - $combinedPath = if ($overridePath) { "${overridePath}:${ldLibraryPath}" } else { $ldLibraryPath } - - $dllFileUnix = Convert-ToUnixPath $dllFileInternal - Write-BuildInfo "DLL file unix: $dllFileUnix" - - # Get the list of dependent DLLs using ldd - Write-BuildInfo "Dry run ldd command: " - & $env:GitBash -c "PATH=${combinedPath}:`$PATH ldd $dllFileUnix" - - # Construct the LDD string - $bashCommand = if ($combinedPath) { "PATH=${combinedPath}:`$PATH ldd $dllFileUnix" } else { "ldd $dllFileUnix" } - - $lddOutput = & $env:GitBash -c "$bashCommand" - - $lddLines = $lddOutput -split "`n" - $dllPaths = @() - foreach ($line in $lddLines) { - if ($line -match "=>\s+(\/[a-z]\/.+\.dll)") { - if ($matches.Count -gt 1) { - $dllPath = $matches[1] - $dllName = [System.IO.Path]::GetFileName($dllPath) - - # we don't need system DLLs, so drop 'em if name starts with api- - if ($dllName -match "^api-") { - continue - } - # mingw64, lib, usr are also system paths, so skip them - if ($dllPath -match "^\/usr\/" -or $dllPath -match "^\/lib\/" -or $dllPath -match "^\/mingw64\/") { - continue - } - - $dllPath = $dllPath -replace "/", "\" - - if ($dllPath -match "^\\c\\Windows\\System32" -or $dllPath -match "^\\c\\Windows\\SysWOW64") { - foreach ($dir in $dllDirectories) { - $candidatePath = Join-Path -Path $dir -ChildPath $dllName - if (Test-Path -Path $candidatePath) { - Write-BuildInfo "Overriding system DLL with version from '$dir': $candidatePath" - $dllPath = $candidatePath - break - } - } - } - - # Skip system paths and include msodbcsql17.dll and other specific DLLs - $dllsToRestore = @("msodbcsql17.dll", "OpenCL.dll", "libcrypto*", "libssl*") - $shouldInclude = $false - foreach ($pattern in $dllsToRestore) { - if ($dllName -like $pattern) { - $shouldInclude = $true - break - } - } - if ($dllPath -notmatch "^\\c\\Windows" -or $shouldInclude) { - if ($dllPath -match "^\\([a-z])\\") { - $dllPath = $dllPath -replace "^\\([a-z])\\", { "$($matches[1].ToUpper()):\" } - } - Write-BuildInfo "Found candidate for copy: $dllPath" - $dllPaths += $dllPath - } - } - } - } - - $fileName = [System.IO.Path]::GetFileName($dllFileInternal) - $destinationPath = Join-Path -Path $destinationDir -ChildPath $fileName - Copy-Item -Path $dllFileInternal -Destination $destinationPath -Force - $dllProcessed[$fileName] = $true - Write-BuildInfo "Copied main DLL: $dllFileInternal => $dllPaths" - - # Copy each dependent DLL to the destination directory - foreach ($dllPathLocal in $dllPaths) { - $fileName = [System.IO.Path]::GetFileName($dllPathLocal) - $destinationPath = Join-Path -Path $destinationDir -ChildPath $fileName - - if (-Not (Test-Path -Path $destinationPath) -or (-not $dllProcessed.ContainsKey($fileName))) { - Copy-Item -Path $dllPathLocal -Destination $destinationPath -Force - Write-Output "$targetDll > Copied: $dllPathLocal to $destinationPath" - - $dllProcessed[$fileName] = $true - } - } -} - -function Get-CollectDeps { - param ( - [string] $dllFile, - [string] $destinationDir - ) - - Set-GdalVariables - $env:GDAL_DATA = "$env:GDAL_INSTALL_DIR\share\gdal" - $env:GDAL_DRIVER_PATH = "$env:GDAL_INSTALL_DIR\share\gdal" - $env:PROJ_LIB = "$env:PROJ_INSTALL_DIR\share\proj" - - $dllDirectories = @("$env:GDAL_INSTALL_DIR\bin", "$env:PROJ_INSTALL_DIR\bin", "$env:VCPKG_INSTALLED\bin", "$env:SDK_PREFIX\bin", $env:VCToolsRedistDir + "x64\") - Write-BuildInfo "Using DLL directories: $dllDirectories" - - Write-BuildInfo "Collecting dependent DLLs for $dllFile" - - Write-BuildInfo "Destination directory: $destinationDir" - - if (-Not (Test-Path -Path $destinationDir)) { - New-Item -ItemType Directory -Path $destinationDir - } - - # Start the recursive copying process - Copy-DependentDLLs -dllFileInternal $dllFile -destinationDir $destinationDir -dllDirectories $dllDirectories - - Write-BuildInfo "All dependent DLLs have been copied to $destinationDir" -} - -function Write-GdalFormats { - Set-GdalVariables - - $dllDirectories = @("$env:GDAL_INSTALL_DIR\bin", "$env:VCPKG_INSTALLED\bin", "$env:SDK_PREFIX\bin", "$env:PROJ_INSTALL_DIR\bin", "$webpRoot\bin") - $originalPath = [System.Environment]::GetEnvironmentVariable("PATH", [System.EnvironmentVariableTarget]::Process) - $newPath = ($dllDirectories -join ";") + ";" + $originalPath - [System.Environment]::SetEnvironmentVariable("PATH", $newPath, [System.EnvironmentVariableTarget]::Process) - - $formats_path = (Get-ForceResolvePath "$PSScriptRoot\..\tests\gdal-formats\formats-win") - New-FolderIfNotExists $formats_path - - Set-Location "$env:GDAL_INSTALL_DIR\bin" - try { - (& .\gdalinfo.exe --formats) | Set-Content $formats_path\gdal-formats-win-raster.txt -Force - (& .\ogrinfo.exe --formats) | Set-Content $formats_path\gdal-formats-win-vector.txt -Force - - # Fix windows style paths in gdal-config - Write-FixShellScriptOnWindows -shellScriptPath "$env:GDAL_INSTALL_DIR\bin\gdal-config" -variableName "CONFIG_DEP_LIBS" - - (& $env:GitBash -c "./gdal-config --formats") | Set-Content -Path "$formats_path\gdal-formats-win.txt" -Force - } - catch { - Write-BuildError "Failed to write GDAL formats" - Write-BuildError $_.Exception.Message - } - - # Restore the original PATH - [System.Environment]::SetEnvironmentVariable("PATH", $originalPath, [System.EnvironmentVariableTarget]::Process) -} - -function Write-FixShellScriptOnWindows { - param ( - [string] $shellScriptPath, - [string] $variableName - ) - $shellScriptContent = Get-Content -Path $shellScriptPath -Raw - $pattern = [regex]::Escape($variableName) + '="([^""].*)"' - if ($shellScriptContent -match $pattern) { - $value = $matches[1] - if ($value.Contains("\(") -or $value.Contains('\"')) { - Write-Output "Variable $variableName already fixed in the script." - return - } - $paths = $value -split ' (?=(?:[^"]*"[^"]*")*[^"]*$)' - $escapedPaths = $paths | ForEach-Object { - $_.Replace(" ", "\\ ").Replace("(", "\(").Replace(")", "\)").Replace('"', '\"'); - } - $escapedValue = ($escapedPaths -join ' ') - $newLine = "$variableName=""$escapedValue""" - $shellScriptContent = $shellScriptContent -replace "$variableName=""[^""].*""", $newLine - Set-Content -Path $shellScriptPath -Value $shellScriptContent - - Write-Output "Shell script has been updated successfully." - - } - else { - Write-Output "Variable $variableName not found in the script." - } -} +function Set-GdalVariables { + $sharedConfig = Get-ForceResolvePath "$PSScriptRoot\..\shared\GdalCore.opt" + $gdalVersion = (Select-String -Path $sharedConfig -Pattern '^GDAL_VERSION=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() + $projVersion = (Select-String -Path $sharedConfig -Pattern '^PROJ_VERSION=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() + $vcpkgCommit = (Select-String -Path $sharedConfig -Pattern '^VCPKG_COMMIT_VER=(.+)$' | Select-Object -First 1).Matches.Groups[1].Value.Trim() + + $env:GDAL_VERSION = $gdalVersion + $env:GDAL_COMMIT_VER = "v$gdalVersion" + $env:PROJ_VERSION = $projVersion + $env:PROJ_COMMIT_VER = $projVersion + $env:VCPKG_COMMIT_VER = $vcpkgCommit + + $env:VS_VERSION = "Visual Studio 17 2022" + $env:SDK = "release-1930-x64" #2022 x64 + $env:SDK_ZIP = "$env:SDK" + "-dev.zip" + $env:SDK_URL = "http://download.gisinternals.com/sdk/downloads/$env:SDK_ZIP" + $env:LIBWEBP_URL = "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.3.2-windows-x64.zip" + $env:LIBZSTD_URL = "https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-v1.5.5-win64.zip" + $env:LIBDEFLATE_URL = "https://github.com/ebiggers/libdeflate/releases/download/v1.19/libdeflate-1.19-windows-x86_64-bin.zip" + $env:VS_VER = "2022" + $env:ARCHITECTURE = "amd64" + $env:WIN64_ARG = "WIN64=YES" + $env:CMAKE_ARCHITECTURE = "x64" + $env:CMAKE_PARALLEL_JOBS = 4 + + $env:GitBash = "C:\Program Files\Git\bin\bash.exe" + $env:BUILD_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\build-win") + $env:PROJ_INSTALL_DIR = (Get-ForceResolvePath "$env:BUILD_ROOT\proj-build") + $env:DOWNLOADS_DIR = (Get-ForceResolvePath "$env:BUILD_ROOT\downloads") + $env:SDK_PREFIX = "$env:BUILD_ROOT\sdk\$env:SDK" + $env:GDAL_SOURCE = "$env:BUILD_ROOT\gdal-source" + $env:PROJ_SOURCE = "$env:BUILD_ROOT\proj-source" + $env:GDAL_DATA = "$env:GDAL_INSTALL_DIR\share\gdal" + $env:GDAL_DRIVER_PATH = "$env:GDAL_INSTALL_DIR\share\gdal" + $env:PROJ_LIB = "$env:PROJ_INSTALL_DIR\share\proj" + $env:GdalCmakeBuild = "$env:BUILD_ROOT\gdal-cmake-temp" + $env:SDK_LIB = "$env:SDK_PREFIX\lib" + $env:SDK_BIN = "$env:SDK_PREFIX\bin" + $env:GDAL_INSTALL_DIR = "$env:BUILD_ROOT\gdal-build" + $env:VCPKG_MANIFEST_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\shared") + $env:VCPKG_INSTALL_ROOT = "$env:BUILD_ROOT\vcpkg\installed" + $env:VCPKG_LOCKFILE = "$env:VCPKG_MANIFEST_ROOT\vcpkg-lock.json" + $env:VCPKG_INSTALLED = "$env:VCPKG_INSTALL_ROOT\x64-windows" + $env:VCPKG_INSTALLED_PKGCONFIG = "$env:VCPKG_INSTALLED\lib\pkgconfig" + + $env:WEBP_ROOT = Get-ForceResolvePath("$env:BUILD_ROOT\sdk\libwebp*") + $env:BUILD_ROOT = (Get-ForceResolvePath "$PSScriptRoot\..\build-win") + $env:7Z_ROOT = (Get-ForceResolvePath "$env:BUILD_ROOT\7z") + Add-EnvPath $env:7Z_ROOT + $env:VCPKG_ROOT_GDAL = (Get-ForceResolvePath "$env:BUILD_ROOT\vcpkg") + Add-EnvPath $env:VCPKG_ROOT_GDAL -Prepend +} + +function Get-WindowsBuildSignature { + param ( + [Parameter(Mandatory = $true)] + [string] $Component + ) + + Set-GdalVariables + + $root = Get-ForceResolvePath "$PSScriptRoot\.." + $commonInputs = Get-FileSignatures -Paths @( + "$root\shared\GdalCore.opt", + "$root\win\install.ps1", + "$root\win\partials.psm1", + "$root\win\vcpkg-makefile.vc", + "$root\shared\vcpkg.json", + "$root\shared\vcpkg-configuration.json", + "$root\shared\vcpkg-lock.json", + "$root\shared\patch\CMakeLists.txt.patch" + ) + + switch ($Component) { + "vcpkg" { + $componentData = @( + "component=vcpkg", + "vcpkgCommit=$env:VCPKG_COMMIT_VER", + "triplet=x64-windows" + ) + } + "proj" { + $componentData = @( + "component=proj", + "projVersion=$env:PROJ_VERSION", + "projSource=$(Get-GitHeadOrDefault -RepositoryPath $env:PROJ_SOURCE -DefaultValue $env:PROJ_COMMIT_VER)" + ) + } + "gdal" { + $componentData = @( + "component=gdal", + "gdalVersion=$env:GDAL_VERSION", + "gdalSource=$(Get-GitHeadOrDefault -RepositoryPath $env:GDAL_SOURCE -DefaultValue $env:GDAL_COMMIT_VER)" + ) + } + default { + throw "Unknown build component: $Component" + } + } + + return (@($componentData) + @($commonInputs)) -join "`n" +} + +function Test-WindowsBuildReuse { + param ( + [Parameter(Mandatory = $true)] + [string] $Component, + [Parameter(Mandatory = $true)] + [string[]] $RequiredPaths, + [Parameter(Mandatory = $true)] + [string] $Message + ) + + $signature = Get-WindowsBuildSignature -Component $Component + if (Test-BuildStateStamp -Component $Component -Signature $signature -RequiredPaths $RequiredPaths) { + Write-BuildStep $Message + return $true + } + + return $false +} + +function Save-WindowsBuildReuse { + param ( + [Parameter(Mandatory = $true)] + [string] $Component + ) + + Save-BuildStateStamp -Component $Component -Signature (Get-WindowsBuildSignature -Component $Component) +} + +function Get-7ZipInstallation { + Write-BuildStep "Checking for 7z installation" + New-FolderIfNotExists $env:7Z_ROOT + $env:7Z_URL = "https://www.7-zip.org/a/7z2501-extra.7z" + if (-Not (Test-Path -Path "$env:7Z_ROOT\7za.exe" -PathType Leaf)) { + Invoke-WebRequest "$env:7Z_URL" -OutFile "$env:7Z_ROOT\7z.7z" + Expand-PscxArchive -Path "$env:7Z_ROOT\7z.7z" -OutputPath "$env:7Z_ROOT\" + Write-BuildStep "Installed 7z into $env:7Z_ROOT" + } + else { + Write-BuildStep "7z is available at $env:7Z_ROOT" + } + $env:PATH += ";$env:7Z_ROOT" +} + +function Get-GdalSdkIsAvailable { + New-FolderIfNotExistsAndSetCurrentLocation $env:DOWNLOADS_DIR + Write-BuildStep "Checking for GDAL SDK from GisInternals" + if (-Not (Test-Path -Path $env:SDK_ZIP -PathType Leaf)) { + Write-BuildInfo "Downloading GDAL SDK from GisInternals" + Invoke-WebRequest "$env:SDK_URL" -OutFile "$env:SDK_ZIP" + Write-BuildStep "GDAL SDK was downloaded" + } + New-FolderIfNotExistsAndSetCurrentLocation "$env:BUILD_ROOT\sdk" + + if (-Not (Test-Path -Path "$env:BUILD_ROOT\sdk\release-*" -PathType Container)) { + exec { 7za x "$env:DOWNLOADS_DIR\$env:SDK_ZIP" -aoa } + } +} + +function Resolve-GdalThidpartyLibs { + New-FolderIfNotExistsAndSetCurrentLocation $env:DOWNLOADS_DIR + Write-BuildStep "Checking for ZSTD, Webp, Deflate libraries" + $env:LIBZSTD_ZIP = "libzstd.zip" + if (-Not (Test-Path -Path $env:LIBZSTD_ZIP -PathType Leaf)) { + Invoke-WebRequest "$env:LIBZSTD_URL" -OutFile "$env:LIBZSTD_ZIP" + } + $env:LIBWEBP_ZIP = "libwebp.zip" + if (-Not (Test-Path -Path $env:LIBWEBP_ZIP -PathType Leaf)) { + Invoke-WebRequest "$env:LIBWEBP_URL" -OutFile "$env:LIBWEBP_ZIP" + } + $env:LIBDEFLATE_ZIP = "libdeflate.zip" + if (-Not (Test-Path -Path $env:LIBDEFLATE_ZIP -PathType Leaf)) { + Invoke-WebRequest "$env:LIBDEFLATE_URL" -OutFile "$env:LIBDEFLATE_ZIP" + } + + New-FolderIfNotExistsAndSetCurrentLocation "$env:BUILD_ROOT\sdk" + exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBWEBP_ZIP" -aoa } + exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBDEFLATE_ZIP" -aoa } + exec { 7za x "$env:DOWNLOADS_DIR\$env:LIBZSTD_ZIP" -aoa } +} +function Get-VcpkgInstallation { + param ( + [bool] $bootstrapVcpkg = $true + ) + + # Force VCPKG_ROOT to a workspace-local path to avoid VS's bundled vcpkg under Program Files + if (-not $env:VCPKG_ROOT_GDAL -or ($env:VCPKG_ROOT_GDAL -notlike "$env:BUILD_ROOT*")) { + $env:VCPKG_ROOT_GDAL = (Get-ForceResolvePath "$env:BUILD_ROOT\vcpkg") + } + + if ($null -eq $env:VCPKG_DEFAULT_BINARY_CACHE) { + $env:VCPKG_DEFAULT_BINARY_CACHE = "$env:BUILD_ROOT\vcpkg-cache" + } + New-FolderIfNotExists $env:VCPKG_DEFAULT_BINARY_CACHE + + if (Test-WindowsBuildReuse -Component "vcpkg" ` + -RequiredPaths @( + "$env:VCPKG_ROOT_GDAL\vcpkg.exe", + "$env:VCPKG_INSTALLED\include", + "$env:VCPKG_INSTALLED\lib" + ) ` + -Message "Reusing cached VCPKG installation and installed packages") { + return + } + + Write-BuildStep "Checking for VCPKG installation" + if ($bootstrapVcpkg) { + Get-CloneAndCheckoutCleanGitRepo https://github.com/Microsoft/vcpkg.git master $env:VCPKG_ROOT_GDAL + } + if (-Not (Test-Path -Path "$env:VCPKG_ROOT_GDAL/vcpkg.exe" -PathType Leaf)) { + Write-BuildInfo "Installing VCPKG" + exec { & "$env:VCPKG_ROOT_GDAL\bootstrap-vcpkg.bat" } + } + else { + Write-BuildInfo "VCPKG is already installed" + } +} + +function Install-VcpkgPackagesSharedConfig { + param ( + [bool] $installVcpkgPackages = $true + ) + + if ($installVcpkgPackages) { + if (Test-WindowsBuildReuse -Component "vcpkg" ` + -RequiredPaths @( + "$env:VCPKG_ROOT_GDAL\vcpkg.exe", + "$env:VCPKG_INSTALLED\include", + "$env:VCPKG_INSTALLED\lib" + ) ` + -Message "Skipping VCPKG manifest install because cached packages are up to date") { + return + } + + Write-BuildInfo "Installing Windows dependencies from shared manifest $env:VCPKG_MANIFEST_ROOT\vcpkg.json with lock $env:VCPKG_LOCKFILE" + exec { & nmake -f ./vcpkg-makefile.vc install_requirements } + Save-WindowsBuildReuse -Component "vcpkg" + } +} + +function Install-Proj { + param ( + [bool] $cleanProjBuild = $true, + [bool] $cleanProjIntermediate = $true + ) + + if (Test-WindowsBuildReuse -Component "proj" ` + -RequiredPaths @( + "$env:PROJ_INSTALL_DIR\include\proj.h", + "$env:PROJ_INSTALL_DIR\lib\proj.lib" + ) ` + -Message "Skipping PROJ rebuild because cached outputs are up to date") { + return + } + + Write-BuildStep "Building PROJ" + if ($cleanProjBuild) { + Write-BuildInfo "Cleaning PROJ build folder" + if (Test-Path -Path "$env:BUILD_ROOT\proj-build" -PathType Container) { + Remove-Item -Path "$env:BUILD_ROOT\proj-build" -Recurse -Force -ErrorAction SilentlyContinue + } + } + Write-BuildInfo "Checking out PROJ repo..." + + Set-Location "$PSScriptRoot" + nmake -f fetch-makefile.vc fetch-proj + + $env:ProjCmakeBuild = "$env:BUILD_ROOT\proj-cmake-temp" + if ($cleanProjIntermediate) { + Write-BuildInfo "Cleaning GDAL intermediate folder" + Remove-Item -Path $env:ProjCmakeBuild -Recurse -Force -ErrorAction SilentlyContinue + } + if ((Test-Path -Path "$env:ProjCmakeBuild\CMakeCache.txt" -PathType Leaf)) { + Write-BuildInfo "Removing build cache (CMakeCache.txt)" + Remove-Item "$env:ProjCmakeBuild\CMakeCache.txt" -ErrorAction SilentlyContinue + } + + Write-BuildInfo "Configuring PROJ..." + New-FolderIfNotExistsAndSetCurrentLocation $env:ProjCmakeBuild + + cmake -G "$env:VS_VERSION" -A $env:CMAKE_ARCHITECTURE "${env:PROJ_SOURCE}" ` + -DCMAKE_INSTALL_PREFIX="${env:PROJ_INSTALL_DIR}" ` + -DCMAKE_BUILD_TYPE=Release -Wno-dev ` + -DCMAKE_C_FLAGS="/w" ` + -DCMAKE_CXX_FLAGS="/w" ` + -DBUILD_TESTING=OFF ` + -DCMAKE_TOOLCHAIN_FILE="${env:VCPKG_ROOT_GDAL}\scripts\buildsystems\vcpkg.cmake" ` + -DCMAKE_PREFIX_PATH="${env:VCPKG_INSTALLED};${env:SDK_PREFIX}" ` + -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF ` + -DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF ` + -DBUILD_SHARED_LIBS=ON -DENABLE_CURL=ON -DENABLE_TIFF=ON ` + + exec { cmake --build . -j $env:CMAKE_PARALLEL_JOBS --config Release --target install } + Write-BuildStep "Done building PROJ" + Save-WindowsBuildReuse -Component "proj" +} + +function Get-GdalVersion { + return (Get-Content "$env:GDAL_SOURCE\VERSION") +} + +function Reset-GdalSourceBindings { + + # remove swig/csharp/[gdal|ogr|osr|const]/obj folders + $env:GdalCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\gdal\obj" + $env:OgrCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\ogr\obj" + $env:OsrCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\osr\obj" + $env:ConstCsharpBindings = "$env:GdalCmakeBuild\swig\csharp\const\obj" + + Write-BuildInfo "Cleaning up GDAL source bindings..." + Remove-Item -Path $env:GdalCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path $env:OgrCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path $env:OsrCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path $env:ConstCsharpBindings -Recurse -Force -ErrorAction SilentlyContinue + Write-BuildInfo "GDAL source bindings were cleaned up" +} + +function Build-Gdal { + param ( + [bool] $cleanGdalBuild = $true, + [bool] $cleanGdalIntermediate = $true, + [bool] $fetchGdal = $true + ) + Set-GdalVariables + $env:PATH = "$env:BUILD_ROOT\proj-build\bin;$env:SDK_BIN;$env:PATH" + $env:CMAKE_INSTALL_PREFIX = "-DCMAKE_INSTALL_PREFIX=$env:GDAL_INSTALL_DIR" + $env:PROJ_ROOT = "-DPROJ_ROOT=$env:PROJ_INSTALL_DIR" + $env:MYSQL_LIBRARY = "-DMYSQL_LIBRARY=$env:SDK_LIB\libmysql.lib" + $env:WEBP_INCLUDE = "-DWEBP_INCLUDE_DIR=$env:WEBP_ROOT\include" + $env:WEBP_LIB = "-DWEBP_LIBRARY=$env:WEBP_ROOT\lib\libwebp.lib" + + $env:Poppler_INCLUDE_DIR = "-DPoppler_INCLUDE_DIR=$env:VCPKG_INSTALLED\include\poppler" + $env:Poppler_LIBRARY = "-DPoppler_LIBRARY=$env:VCPKG_INSTALLED\lib\poppler.lib" + $env:TIFF_INCLUDE_DIR = "-DTIFF_INCLUDE_DIR=$env:VCPKG_INSTALLED\include\tiff" + $env:TIFF_LIBRARY = "-DTIFF_LIBRARY_RELEASE=$env:VCPKG_INSTALLED\lib\tiff.lib" + + # libpq must resolve from vcpkg, not from the GisInternals SDK: the SDK still + # ships PostgreSQL 14.0 (see issue #241). PostgreSQL_ROOT is searched ahead of + # CMAKE_PREFIX_PATH. Deliberately not named $env:PostgreSQL_ROOT - CMake also + # reads that as an environment variable and would pick up the "-D..." text. + # GDAL_USE_POSTGRESQL=ON below only catches "not found at all"; the packaged-DLL + # check after Get-CollectDeps is what catches "found the SDK copy instead". + $env:PG_ROOT_ARG = "-DPostgreSQL_ROOT=$env:VCPKG_INSTALLED" + + # HDF5, netCDF-C and CFITSIO must resolve from vcpkg, not from the GisInternals + # SDK (see issue #246). HDF4 (hdf.dll, mfhdf.dll, xdr.dll) intentionally stays + # from the SDK - it has no vcpkg port. + # + # hdf5 is installed WITH szip (default feature, via libaec) and the cpp feature + # (GDAL requests CXX when KEA is detected, even with GDAL_USE_KEA=OFF). On + # MSVC, libaec produces aec.dll + szip.dll; the latter replaces the SDK's + # szip.dll because VCPKG_INSTALLED\bin is searched before SDK_PREFIX\bin. This + # is safe: libaec's szip.dll is a drop-in implementing the SZ_* API that the + # SDK HDF4 (hdf.dll / mfhdf.dll) imports, matching what Debian does with + # libhdf4 + libaec. + # + # HDF5_ROOT restricts FindHDF5 to vcpkg. Without it, FindHDF5 mixes SDK + # headers (1.12.0) with vcpkg libraries (2.x) - an ABI-incompatible combination. + # HDF5_ROOT alone makes FindHDF5 find the 2.x config file and read the version, + # but HDF5 2.x's hdf5-config.cmake sets variables with a lowercase "hdf5_" + # prefix that FindHDF5 doesn't map to the expected uppercase "HDF5_INCLUDE_DIR", + # leaving HDF5_INCLUDE_DIRS empty. Pre-setting HDF5_C_INCLUDE_DIR and + # HDF5_CXX_INCLUDE_DIR as cache variables bypasses the broken extraction. + $env:HDF5_ROOT_ARG = "-DHDF5_ROOT=$env:VCPKG_INSTALLED" + $env:HDF5_C_INCLUDE_ARG = "-DHDF5_C_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" + $env:HDF5_CXX_INCLUDE_ARG = "-DHDF5_CXX_INCLUDE_DIR:PATH=$env:VCPKG_INSTALLED\include" + $env:NETCDF_ROOT_ARG = "-DnetCDF_ROOT=$env:VCPKG_INSTALLED" + $env:CFITSIO_ROOT_ARG = "-DCFITSIO_ROOT=$env:VCPKG_INSTALLED" + + if (Test-WindowsBuildReuse -Component "gdal" ` + -RequiredPaths @( + "$env:GDAL_INSTALL_DIR\bin\gdal.dll", + "$env:GdalCmakeBuild\swig\csharp" + ) ` + -Message "Skipping GDAL rebuild because cached outputs are up to date") { + return + } + + Write-BuildStep "Configuring GDAL" + Set-Location "$env:BUILD_ROOT" + + if ($cleanGdalBuild) { + Write-BuildInfo "Cleaning GDAL build folder" + if (Test-Path -Path "$env:BUILD_ROOT\gdal-build" -PathType Container) { + Remove-Item -Path "$env:BUILD_ROOT\gdal-build" -Recurse -Force -ErrorAction SilentlyContinue + } + } + + if ($cleanGdalIntermediate) { + Write-BuildInfo "Cleaning GDAL intermediate folder" + Remove-Item -Path $env:GdalCmakeBuild -Recurse -Force -ErrorAction SilentlyContinue + } + + Set-Location "$PSScriptRoot" + + if ($fetchGdal) { + Write-BuildInfo "Fetching GDAL source" + exec { nmake -f fetch-makefile.vc fetch-gdal } + } + + if ((Test-Path -Path "$env:GdalCmakeBuild\CMakeCache.txt" -PathType Leaf)) { + Write-BuildInfo "Removing build cache (CMakeCache.txt)" + Remove-Item "$env:GdalCmakeBuild\CMakeCache.txt" -ErrorAction SilentlyContinue + } + + # PATCH 1: replace build root of SDK with our own + Set-ReplaceContentInFiles -Path $env:SDK_PREFIX ` + -FileFilter "*.pc", "*.cmake", "*.opt" ` + -What "E:/buildsystem/$env:SDK", "E:\buildsystem\$env:SDK" ` + -With $env:SDK_PREFIX.Replace("\", "/") + + Set-Location "$env:GDAL_SOURCE" + + Remove-Item -Path $env:GDAL_SOURCE/autotest -Recurse -Force -ErrorAction SilentlyContinue + # PATCH 2: apply patch to cmake pipeline. remove redundant compile steps + git apply "$PSScriptRoot\..\shared\patch\CMakeLists.txt.patch" + + New-FolderIfNotExistsAndSetCurrentLocation $env:GdalCmakeBuild + New-FolderIfNotExists "$PSScriptRoot\..\nuget" + + $env:ARCH_FLAGS = "/arch:AVX2 /Ob2 /Oi /Os /Oy /w" + # disabling KEA driver as it causes build issues on Windows + # https://github.com/OSGeo/gdal/blob/3b232ee17d8f3d93bf3535b77fbb436cb9a9c2e0/.github/workflows/windows_build.yml#L178 + + # for the same reason, we are disabling OpenEXR + # -DOpenEXR_LIBRARY="$env:VCPKG_INSTALLED\lib\OpenEXR-3_2.lib" ` + # -DOpenEXR_INCLUDE_DIR="$env:VCPKG_INSTALLED\include\OpenEXR" ` + # -DOpenEXR_UTIL_LIBRARY="$env:VCPKG_INSTALLED\lib\OpenEXRUtil-3_2.lib" ` + # -DOpenEXR_HALF_LIBRARY="$env:VCPKG_INSTALLED\lib\Imath-3_1.lib" ` + # -DOpenEXR_IEX_LIBRARY="$env:VCPKG_INSTALLED\lib\Iex-3_2.lib" ` + + cmake -G "$env:VS_VERSION" -A $env:CMAKE_ARCHITECTURE "$env:GDAL_SOURCE" ` + $env:CMAKE_INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release -Wno-dev ` + -DCMAKE_C_FLAGS="$env:ARCH_FLAGS" ` + -DCMAKE_CXX_FLAGS="$env:ARCH_FLAGS" ` + -DCMAKE_PREFIX_PATH="${env:VCPKG_INSTALLED};${env:SDK_PREFIX}" ` + -DCMAKE_FIND_USE_PACKAGE_REGISTRY=OFF ` + -DCMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF ` + -DGDAL_USE_OPENEXR=OFF ` + $env:WEBP_INCLUDE $env:WEBP_LIB ` + $env:PROJ_ROOT $env:MYSQL_LIBRARY ` + $env:Poppler_INCLUDE_DIR $env:Poppler_LIBRARY ` + $env:PG_ROOT_ARG ` + -DGDAL_USE_POSTGRESQL=ON ` + $env:HDF5_ROOT_ARG ` + $env:HDF5_C_INCLUDE_ARG ` + $env:HDF5_CXX_INCLUDE_ARG ` + $env:NETCDF_ROOT_ARG ` + $env:CFITSIO_ROOT_ARG ` + -DGDAL_USE_HDF5=ON ` + -DGDAL_USE_NETCDF=ON ` + -DGDAL_USE_CFITSIO=ON ` + -DGDAL_USE_KEA=OFF ` + -DGDAL_USE_ZLIB_INTERNAL=ON ` + -DGDAL_CSHARP_APPS=ON ` + -DGDAL_CSHARP_TESTS=OFF ` + -DGDAL_CSHARP_BUILD_NUPKG=OFF ` + -DBUILD_CSHARP_BINDINGS=ON ` + -DBUILD_JAVA_BINDINGS=OFF ` + -DBUILD_PYTHON_BINDINGS=OFF + + # Fail early if cmake resolved any HDF5, netCDF or CFITSIO path from the SDK + # instead of vcpkg (#246). Catches mixed-ABI configurations that the DLL + # assertions alone cannot detect (e.g. SDK headers with vcpkg libraries). + $cacheFile = "$env:GdalCmakeBuild\CMakeCache.txt" + $leaked = Select-String -Path $cacheFile -Pattern "^(HDF5|netCDF|CFITSIO).*=.*$([regex]::Escape($env:SDK))" | + Where-Object { $_.Line -notmatch "^#" } + if ($leaked) { + $leaked | ForEach-Object { Write-BuildError $_.Line } + throw "CMake resolved an HDF5/netCDF/CFITSIO path from the SDK (see #246)." + } + Write-BuildStep "Verified HDF5/netCDF/CFITSIO cache entries point to vcpkg" + + Write-BuildStep "Building GDAL" + exec { cmake --build . -j $env:CMAKE_PARALLEL_JOBS --config Release --target install } + + # remove source. this was added by GDAL + exec { dotnet nuget remove source local } + Write-BuildStep "GDAL was built successfully" + Save-WindowsBuildReuse -Component "gdal" +} + +function Build-GenerateProjectFiles { + param ( + [string] $packageVersion, + [bool] $preRelease = $false + ) + Set-GdalVariables + Set-Location "$PSScriptRoot/../unix" + $vcpkgInstalled = Get-PathRelative -inputPath:$env:VCPKG_INSTALLED_PKGCONFIG -relativePath:"../build-win" + $geosVersion = (& $env:GitBash -c "make -f generate-projects-makefile get-version IN_FILE=$("$vcpkgInstalled/geos.pc")") + + $preReleaseVal = if ($preRelease) { "true" } else { "false" } + + # generate project files for C# bindings + Write-BuildStep "Generating project files for GDAL C# bindings" + exec { & $env:GitBash -c "make -f generate-projects-makefile CAT_NAME=win RUNTIME_PACKAGE_PARTIAL=WindowsRuntime TARGET_OS=win BUILD_NUMBER_TAIL=$packageVersion GEOS_VERSION=$geosVersion BUILD_ARCH=$env:CMAKE_ARCHITECTURE PRERELEASE=$preReleaseVal" } + Write-BuildStep "Done generating project files" +} + +function Build-CsharpBindings { + param ( + [bool] $isDebug = $false, + [bool] $preRelease = $false, + [string] $packageVersion + ) + Set-GdalVariables + Write-BuildStep "Building GDAL C# bindings" + + Get-VisualStudioVars + + Set-Location $PSScriptRoot + + Write-GdalFormats + + Set-Location $PSScriptRoot + + $outputPath = & nmake -f collect-deps-makefile.vc get-output + Write-BuildInfo "Output path: $outputPath" + exec { & nmake -f collect-deps-makefile.vc } + + Get-CollectDeps "$env:GDAL_INSTALL_DIR\bin\gdal.dll" "$outputPath" + + # Copy-DependentDLLs falls back to $env:SDK_PREFIX\bin, so a failed vcpkg + # resolution would silently repackage the SDK's PostgreSQL 14.0 libpq and CI + # would still pass. Refuse to ship anything but the vcpkg build (#241). + $vcpkgPq = "$env:VCPKG_INSTALLED\bin\libpq.dll" + $packagedPq = Join-Path "$outputPath" "libpq.dll" + if (-not (Test-Path $vcpkgPq) -or -not (Test-Path $packagedPq) -or + (Get-FileHash $packagedPq).Hash -ne (Get-FileHash $vcpkgPq).Hash) { + throw "Packaged libpq.dll is not the vcpkg build. Expected a copy of $vcpkgPq (see #241)." + } + Write-BuildStep "Verified packaged libpq.dll comes from vcpkg" + + # Same guard for HDF5, netCDF and CFITSIO (#246). Copy-DependentDLLs falls back + # to SDK_PREFIX\bin, which still carries the old SDK builds. + foreach ($dllName in @("hdf5.dll", "hdf5_hl.dll", "netcdf.dll", "cfitsio.dll", "szip.dll", "aec.dll")) { + $vcpkgDll = "$env:VCPKG_INSTALLED\bin\$dllName" + $packagedDll = Join-Path "$outputPath" $dllName + if (-not (Test-Path $vcpkgDll) -or -not (Test-Path $packagedDll) -or + (Get-FileHash $packagedDll).Hash -ne (Get-FileHash $vcpkgDll).Hash) { + throw "Packaged $dllName is not the vcpkg build. Expected a copy of $vcpkgDll (see #246)." + } + } + # gdal.dll links the HDF5 C++ library but imports nothing from it, so hdf5_cpp.dll + # is normally not collected. If it ever is, it must still be the vcpkg build. + $vcpkgHdf5Cpp = "$env:VCPKG_INSTALLED\bin\hdf5_cpp.dll" + $packagedHdf5Cpp = Join-Path "$outputPath" "hdf5_cpp.dll" + if ((Test-Path $packagedHdf5Cpp) -and (-not (Test-Path $vcpkgHdf5Cpp) -or + (Get-FileHash $packagedHdf5Cpp).Hash -ne (Get-FileHash $vcpkgHdf5Cpp).Hash)) { + throw "Packaged hdf5_cpp.dll is not the vcpkg build. Expected a copy of $vcpkgHdf5Cpp (see #246)." + } + Write-BuildStep "Verified packaged HDF5, netCDF and CFITSIO DLLs come from vcpkg" + + Build-GenerateProjectFiles -packageVersion $packageVersion -preRelease $preRelease + + Set-Location $PSScriptRoot + if ($isDebug) { + exec { & nmake -f publish-makefile.vc pack-dev DEBUG=1 PACKAGE_BUILD_NUMBER=$packageVersion } + } + else { + exec { & nmake -f publish-makefile.vc pack PACKAGE_BUILD_NUMBER=$packageVersion } + } +} + +function Convert-ToUnixPath($path) { + $unixPath = $path -replace "\\", "/" + if ($unixPath -match "^([a-zA-Z]):") { + $unixPath = $unixPath -replace "^([a-zA-Z]):", { "/$($matches[1].ToLower())" } + } + if ($unixPath -match "\s") { + $unixPath = "`"$unixPath`"" + } + return $unixPath +} + +# Function to find and copy dependent DLLs recursively +function Copy-DependentDLLs { + param ( + [string]$dllFileInternal, + [string[]]$dllDirectories, + [string]$destinationDir, + [string]$overridePath = $null # Optional parameter for path override + ) + $dllProcessed = @{} + $targetDll = [System.IO.Path]::GetFileName($dllFileInternal) + Write-BuildStep "Copying dependent DLLs for $dllFileInternal" + # Convert Windows paths to Unix paths for Git Bash and construct LD_LIBRARY_PATH + $ldLibraryPath = ($dllDirectories | ForEach-Object { Convert-ToUnixPath $_ }) -join ":" + # Use overridePath if provided, otherwise use an empty string + $combinedPath = if ($overridePath) { "${overridePath}:${ldLibraryPath}" } else { $ldLibraryPath } + + $dllFileUnix = Convert-ToUnixPath $dllFileInternal + Write-BuildInfo "DLL file unix: $dllFileUnix" + + # Get the list of dependent DLLs using ldd + Write-BuildInfo "Dry run ldd command: " + & $env:GitBash -c "PATH=${combinedPath}:`$PATH ldd $dllFileUnix" + + # Construct the LDD string + $bashCommand = if ($combinedPath) { "PATH=${combinedPath}:`$PATH ldd $dllFileUnix" } else { "ldd $dllFileUnix" } + + $lddOutput = & $env:GitBash -c "$bashCommand" + + $lddLines = $lddOutput -split "`n" + $dllPaths = @() + foreach ($line in $lddLines) { + if ($line -match "=>\s+(\/[a-z]\/.+\.dll)") { + if ($matches.Count -gt 1) { + $dllPath = $matches[1] + $dllName = [System.IO.Path]::GetFileName($dllPath) + + # we don't need system DLLs, so drop 'em if name starts with api- + if ($dllName -match "^api-") { + continue + } + # mingw64, lib, usr are also system paths, so skip them + if ($dllPath -match "^\/usr\/" -or $dllPath -match "^\/lib\/" -or $dllPath -match "^\/mingw64\/") { + continue + } + + $dllPath = $dllPath -replace "/", "\" + + if ($dllPath -match "^\\c\\Windows\\System32" -or $dllPath -match "^\\c\\Windows\\SysWOW64") { + foreach ($dir in $dllDirectories) { + $candidatePath = Join-Path -Path $dir -ChildPath $dllName + if (Test-Path -Path $candidatePath) { + Write-BuildInfo "Overriding system DLL with version from '$dir': $candidatePath" + $dllPath = $candidatePath + break + } + } + } + + # Skip system paths and include msodbcsql17.dll and other specific DLLs + $dllsToRestore = @("msodbcsql17.dll", "OpenCL.dll", "libcrypto*", "libssl*") + $shouldInclude = $false + foreach ($pattern in $dllsToRestore) { + if ($dllName -like $pattern) { + $shouldInclude = $true + break + } + } + if ($dllPath -notmatch "^\\c\\Windows" -or $shouldInclude) { + if ($dllPath -match "^\\([a-z])\\") { + $dllPath = $dllPath -replace "^\\([a-z])\\", { "$($matches[1].ToUpper()):\" } + } + Write-BuildInfo "Found candidate for copy: $dllPath" + $dllPaths += $dllPath + } + } + } + } + + $fileName = [System.IO.Path]::GetFileName($dllFileInternal) + $destinationPath = Join-Path -Path $destinationDir -ChildPath $fileName + Copy-Item -Path $dllFileInternal -Destination $destinationPath -Force + $dllProcessed[$fileName] = $true + Write-BuildInfo "Copied main DLL: $dllFileInternal => $dllPaths" + + # Copy each dependent DLL to the destination directory + foreach ($dllPathLocal in $dllPaths) { + $fileName = [System.IO.Path]::GetFileName($dllPathLocal) + $destinationPath = Join-Path -Path $destinationDir -ChildPath $fileName + + if (-Not (Test-Path -Path $destinationPath) -or (-not $dllProcessed.ContainsKey($fileName))) { + Copy-Item -Path $dllPathLocal -Destination $destinationPath -Force + Write-Output "$targetDll > Copied: $dllPathLocal to $destinationPath" + + $dllProcessed[$fileName] = $true + } + } +} + +function Get-CollectDeps { + param ( + [string] $dllFile, + [string] $destinationDir + ) + + Set-GdalVariables + $env:GDAL_DATA = "$env:GDAL_INSTALL_DIR\share\gdal" + $env:GDAL_DRIVER_PATH = "$env:GDAL_INSTALL_DIR\share\gdal" + $env:PROJ_LIB = "$env:PROJ_INSTALL_DIR\share\proj" + + $dllDirectories = @("$env:GDAL_INSTALL_DIR\bin", "$env:PROJ_INSTALL_DIR\bin", "$env:VCPKG_INSTALLED\bin", "$env:SDK_PREFIX\bin", $env:VCToolsRedistDir + "x64\") + Write-BuildInfo "Using DLL directories: $dllDirectories" + + Write-BuildInfo "Collecting dependent DLLs for $dllFile" + + Write-BuildInfo "Destination directory: $destinationDir" + + if (-Not (Test-Path -Path $destinationDir)) { + New-Item -ItemType Directory -Path $destinationDir + } + + # Start the recursive copying process + Copy-DependentDLLs -dllFileInternal $dllFile -destinationDir $destinationDir -dllDirectories $dllDirectories + + Write-BuildInfo "All dependent DLLs have been copied to $destinationDir" +} + +function Write-GdalFormats { + Set-GdalVariables + + $dllDirectories = @("$env:GDAL_INSTALL_DIR\bin", "$env:VCPKG_INSTALLED\bin", "$env:SDK_PREFIX\bin", "$env:PROJ_INSTALL_DIR\bin", "$webpRoot\bin") + $originalPath = [System.Environment]::GetEnvironmentVariable("PATH", [System.EnvironmentVariableTarget]::Process) + $newPath = ($dllDirectories -join ";") + ";" + $originalPath + [System.Environment]::SetEnvironmentVariable("PATH", $newPath, [System.EnvironmentVariableTarget]::Process) + + $formats_path = (Get-ForceResolvePath "$PSScriptRoot\..\tests\gdal-formats\formats-win") + New-FolderIfNotExists $formats_path + + Set-Location "$env:GDAL_INSTALL_DIR\bin" + try { + (& .\gdalinfo.exe --formats) | Set-Content $formats_path\gdal-formats-win-raster.txt -Force + (& .\ogrinfo.exe --formats) | Set-Content $formats_path\gdal-formats-win-vector.txt -Force + + # Fix windows style paths in gdal-config + Write-FixShellScriptOnWindows -shellScriptPath "$env:GDAL_INSTALL_DIR\bin\gdal-config" -variableName "CONFIG_DEP_LIBS" + + (& $env:GitBash -c "./gdal-config --formats") | Set-Content -Path "$formats_path\gdal-formats-win.txt" -Force + } + catch { + Write-BuildError "Failed to write GDAL formats" + Write-BuildError $_.Exception.Message + } + + # Restore the original PATH + [System.Environment]::SetEnvironmentVariable("PATH", $originalPath, [System.EnvironmentVariableTarget]::Process) +} + +function Write-FixShellScriptOnWindows { + param ( + [string] $shellScriptPath, + [string] $variableName + ) + $shellScriptContent = Get-Content -Path $shellScriptPath -Raw + $pattern = [regex]::Escape($variableName) + '="([^""].*)"' + if ($shellScriptContent -match $pattern) { + $value = $matches[1] + if ($value.Contains("\(") -or $value.Contains('\"')) { + Write-Output "Variable $variableName already fixed in the script." + return + } + $paths = $value -split ' (?=(?:[^"]*"[^"]*")*[^"]*$)' + $escapedPaths = $paths | ForEach-Object { + $_.Replace(" ", "\\ ").Replace("(", "\(").Replace(")", "\)").Replace('"', '\"'); + } + $escapedValue = ($escapedPaths -join ' ') + $newLine = "$variableName=""$escapedValue""" + $shellScriptContent = $shellScriptContent -replace "$variableName=""[^""].*""", $newLine + Set-Content -Path $shellScriptPath -Value $shellScriptContent + + Write-Output "Shell script has been updated successfully." + + } + else { + Write-Output "Variable $variableName not found in the script." + } +}