diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 147fa95af..14fe1a0de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,15 +62,15 @@ jobs: - name: Check with Clippy run: cargo clippy --all-targets -- -D warnings - name: Check with Clippy (--all-features) - run: cargo clippy --all-targets --all-features -- -D warnings + run: cargo clippy --all-targets --features "default bindgen array" -- -D warnings - name: Build run: cargo build - name: Run tests run: cargo test - name: Build (--all-features) - run: cargo build --all-features + run: cargo build --features "default bindgen array" - name: Run tests (--all-features) - run: cargo test --all-features -- --nocapture + run: cargo test --features "default bindgen array" -- --nocapture ubuntu_lts: name: "ci ubuntu-lts" @@ -105,18 +105,63 @@ jobs: - name: Check with Clippy run: cargo clippy --all-targets -- -D warnings - name: Check with Clippy (--all-features) - run: cargo clippy --all-targets --all-features -- -D warnings + run: cargo clippy --all-targets --features "default bindgen array" -- -D warnings - name: Build run: cargo build - name: Run tests run: cargo test - name: Build (--all-features) - run: cargo build --all-features + run: cargo build --features "default bindgen array" - name: Run tests (--all-features) - run: cargo test --all-features -- --nocapture + run: cargo test --features "default bindgen array" -- --nocapture - name: Install valgrind and cargo-valgrind run: | sudo apt-get install valgrind -y cargo install cargo-valgrind - name: Run --lib tests under valgrind run: cargo valgrind test --lib + gdal_static: + name: "ci gdal-static" + strategy: + matrix: + os: + - ubuntu-latest + - windows-latest + - macos-13 # x86_64 + - macos-14 # aarch64 + runs-on: ${{ matrix.os }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + - name: Install stable + run: | + rustup install --no-self-update --profile minimal stable + rustup component add rustfmt clippy + # we need to have the sqlite binary in path for building proj from source + - name: Install Sqlite (Windows) + if: runner.os == 'Windows' + run: | + choco install sqlite + echo "C:\ProgramData\chocolatey\lib\SQLite\tools" >> $GITHUB_PATH + sqlite3 --version + # use the minimal driver set for clippy as the other + # drivers do not change the rust code + # enable `driver_sqlite` to force statically linking libsqlite3 for proj + - name: Check with Clippy (bundled) + run: cargo clippy --all-targets --features "gdal-sys/bundled gdal-src gdal-src/driver_sqlite" -- -D warnings + # we only build tests here as we have disabled features + # that are required for running tests + - name: Build bundled gdal (minimal features) + if: runner.os == 'Windows' + # we use cargo test --no-run here because + # tests do not pass due to missing libgeos but we want to have a complete build (including linking) + run: cargo test --features "gdal-sys/bundled gdal-src gdal-src/driver_sqlite" --no-run + - name: Build bundled gdal (minimal features) + if: runner.os != 'Windows' + # we use cargo test --no-run here because + # tests do not pass due to missing libgeos but we want to have a complete build (including linking) + run: cargo test --features "gdal-sys/bundled gdal-src" --no-run + - name: Test bundled (all features) + run: cargo test --features "gdal-sys/bundled gdal-src gdal-src/all_drivers gdal-src/geos_static" diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..876130392 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "gdal-src/source"] + path = gdal-src/source + url = https://github.com/OSGeo/gdal diff --git a/CHANGES.md b/CHANGES.md index 6c26d490b..59828232e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ ## Unreleased +- Add a `bundled` feature for `gdal-sys` that allows to build and statically link a minimal bundled version of gdal during `cargo build` + ## 0.17 - Added pre-built bindings for GDAL 3.9 @@ -85,6 +87,7 @@ - + - Added `{Display|FromStr} for ResampleAlg` and `ResampleAlg::iter`. - diff --git a/Cargo.toml b/Cargo.toml index b0033755c..aed795695 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ thiserror = "1.0" libc = "0.2" geo-types = { version = "0.7.11" } gdal-sys = { path = "gdal-sys", version = "0.10" } +gdal-src = { path = "gdal-src", optional = true, default-features = false } ndarray = { version = "0.15", optional = true } chrono = { version = "0.4.26", default-features = false } bitflags = "2.4" @@ -36,7 +37,7 @@ tempfile = "3.8" arrow = { version = "52.0", default-features = false, features = ["ffi"] } [workspace] -members = ["gdal-sys"] +members = ["gdal-src", "gdal-sys"] # docs.rs-specific configuration [package.metadata.docs.rs] @@ -61,3 +62,4 @@ check-cfg = [ 'cfg(minor_ge_8)', 'cfg(minor_ge_9)', ] + diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 000000000..732813eb1 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,17 @@ +# Updating bundled gdal version for gdal-src + +Perform the following steps: + +``` +git submodule init +git submodule update +cd gdal-src/source +git pull +git checkout v3.8.3 # corresponds to the tag you want to update to +cd ../../ +git add gdal-src/source +git commit -m "Update bundled gdal version to 3.8.4" +``` + +These steps assume that there are no fundamental changes to the gdal build system. + diff --git a/gdal-src/Cargo.toml b/gdal-src/Cargo.toml new file mode 100644 index 000000000..e80ad6b0d --- /dev/null +++ b/gdal-src/Cargo.toml @@ -0,0 +1,333 @@ +[package] +name = "gdal-src" +version = "0.1.0" +edition = "2021" +links = "gdal_src" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +link-cplusplus = "1.0" +proj-sys = { version = "0.24.0", features = ["bundled_proj"] } +libsqlite3-sys = { version = "0.28.0", features = ["bundled"], optional = true } +hdf5-sys = { package = "hdf5-metno-sys", version = "0.9.1", optional = true, features = ["static", "hl", "deprecated"] } +netcdf-sys = { version = "0.8.1", optional = true, features = ["static"] } +pq-src = { version = "0.1.3", optional = true } +curl-sys = { version = "0.4.70", features = ["static-curl"], optional = true } +libz-sys = { version = "1.1.15", features = ["static"], optional = true } +geos-sys = { version = "2.0.6", optional = true } +# we need to depend directly on `geos-src` for the static +# build as otherwise `DEP_GEOSSRC_ROOT` is not set +geos-src = { version = "0.2.3", optional = true } + +[build-dependencies] +cmake = "0.1.50" + +[features] +default = [] +# force not building this crate +nobuild = ["proj-sys/nobuild"] + +all_drivers = [ + "internal_drivers", + "driver_sqlite", + "driver_gpkg", + "driver_vfk", + "driver_hdf5", + "driver_netcdf", + "driver_amigocloud", + "driver_carto", + "driver_daas", + "driver_eeda", + "driver_elastic", + "driver_ngw", + "driver_ogcapi", + "driver_plmosaic", + "driver_wcs", + "driver_wms", + "driver_wmts", + "driver_pg", + "driver_postgis_raster", +] + +# note that libgeos is LGPL licenced +# enabling this feature will means +# that you statically link libgeos and +# therefore makes your binary fall under +# LGPL as well +geos_static = ["geos", "dep:geos-sys", "geos-sys?/static", "dep:geos-src"] +# as long as the `geos_static` feature +# is not enabled that will only +# dynamically link geos +geos = ["dep:geos-sys"] + +internal_drivers = [ + # ogr internal, + "driver_avc", + "driver_cad", + "driver_csv", + "driver_dgn", + "driver_dxf", + "driver_edigeo", + "driver_flatgeobuf", + "driver_geoconcept", + "driver_geojson", + "driver_gmt", + "driver_gtfs", + "driver_jsonfg", + "driver_mapml", + "driver_openfilegdb", + "driver_pgdump", + "driver_ntf", + "driver_s57", + "driver_selafin", + "driver_shape", + "driver_sxf", + "driver_tab", + "driver_tiger", + "driver_vdv", + "driver_wasp", + # gdal internal + "driver_aaigrid", + "driver_adrg", + "driver_aigrid", + "driver_airsar", + "driver_blx", + "driver_bmp", + "driver_bsb", + "driver_cals", + "driver_ceos", + "driver_coasp", + "driver_cosar", + "driver_ctg", + "driver_dimap", + "driver_dted", + "driver_elas", + "driver_envisat", + "driver_ers", + "driver_fit", + "driver_gff", + "driver_gif", + "driver_grib", + "driver_gsg", + "driver_gtiff", + "driver_gxf", + "driver_hf2", + "driver_hfa", + "driver_ilwis", + "driver_iris", + "driver_jaxapalsar", + "driver_jdem", + "driver_jpeg", + "driver_kmlsuperoverlay", + "driver_l1b", + "driver_leveller", + "driver_map", + "driver_mrf", + "driver_msgn", + "driver_ngsgeoid", + "driver_nift", + "driver_northwood", + "driver_ozi", + "driver_pcidsk", + "driver_pcraster", + "driver_png", + "driver_prf", + "driver_r", + "driver_raw", + "driver_rik", + "driver_rmf", + "driver_rs2", + "driver_safe", + "driver_saga", + "driver_sar_ceos", + "driver_sentinel2", + "driver_sgi", + "driver_sigdem", + "driver_srtmhgt", + "driver_stacit", + "driver_stacta", + "driver_terragen", + "driver_tga", + "driver_til", + "driver_tsx", + "driver_usgsdem", + "driver_xpm", + "driver_xyz", + "driver_zmap", + + # ogr and gdal + "driver_idrisi", + "driver_sdts", + "driver_vrt", + "driver_mem", +] + +# ogr internal +driver_avc = [] +driver_cad = [] +driver_csv = [] +driver_dgn = [] +driver_dxf = [] +driver_edigeo = [] +driver_flatgeobuf = [] +driver_geoconcept = [] +driver_geojson = [] +driver_gmt = [] +driver_gtfs = [] +driver_jsonfg = [] +driver_mapml = [] +driver_openfilegdb = [] +driver_pgdump = [] +driver_ntf = [] +driver_s57 = [] +driver_selafin = [] +driver_shape = [] +driver_sxf = [] +driver_tab = [] +driver_tiger = [] +driver_vdv = [] +driver_wasp = [] + +#gdal internal +driver_aaigrid = [] +driver_adrg = [] +driver_aigrid = [] +driver_airsar = [] +driver_blx = [] +driver_bmp = [] +driver_bsb = [] +driver_cals = [] +driver_ceos = [] +driver_coasp = [] +driver_cosar = [] +driver_ctg = [] +driver_dimap = [] +driver_dted = [] +driver_elas = [] +driver_envisat =[] +driver_ers = [] +driver_fit = [] +driver_gff = [] +driver_gif = [] +driver_grib = [] +driver_gsg = [] +driver_gtiff = [] +driver_gxf = [] +driver_hf2 = [] +driver_hfa = [] +driver_ilwis = [] +driver_iris = [] +driver_jaxapalsar = [] +driver_jdem = [] +driver_jpeg = [] +driver_kmlsuperoverlay = [] +driver_l1b = [] +driver_leveller = [] +driver_map = [] +driver_mrf = [] +driver_msgn = [] +driver_ngsgeoid = [] +driver_nift = [] +driver_northwood = [] +driver_ozi = [] +driver_pcidsk = [] +driver_pcraster = [] +driver_pds = [] +driver_png = [] +driver_prf = [] +driver_r = [] +driver_raw = [] +driver_rik = [] +driver_rmf = [] +driver_rs2 = [] +driver_safe = [] +driver_saga = [] +driver_sar_ceos = [] +driver_sentinel2 = [] +driver_sgi = [] +driver_sigdem = [] +driver_srtmhgt = [] +driver_stacit = [] +driver_stacta = [] +driver_terragen = [] +driver_tga = [] +driver_til = [] +driver_tsx = [] +driver_usgsdem = [] +driver_xpm = [] +driver_xyz = [] +driver_zmap = [] + +# ogr and gdal +driver_idrisi = [] +driver_sdts = [] +driver_vrt = [] +driver_mem = [] + +# external + +# sqlite +driver_sqlite = ["dep:libsqlite3-sys"] +driver_gpkg = ["driver_sqlite"] +driver_vfk = ["driver_sqlite"] +driver_rasterlite = [] # unclear how to handle that + +# curl +driver_amigocloud = ["curl-sys"] +driver_carto = ["curl-sys"] +driver_daas = ["curl-sys"] +driver_eeda = ["curl-sys"] +driver_elastic = ["curl-sys"] +driver_ngw = ["curl-sys"] + +driver_ogcapi = ["curl-sys"] +driver_plmosaic = ["curl-sys"] +driver_wcs = ["curl-sys"] +driver_wms = ["curl-sys"] +driver_wmts = ["curl-sys"] + +# libexpat +# (there exists no expat-sys crate) +# driver_georss = [] +# driver_gml = [] +# driver_gpsbabel = [] +# driver_gpx = [] +# driver_jml = [] +# driver_kml = [] +# driver_lvbag = [] +# driver_ods = [] +# driver_svg = [] +# driver_xlsx = [] + +# libmysqlclient +# (there is currently no bundling support in libmysqlclient) +# driver_mysql = [] + +# libpq +driver_pg = ["dep:pq-src"] +driver_postgis_raster = ["driver_pg"] + +# libhdf5 +driver_hdf5 = ["dep:hdf5-sys", "hdf5-sys?/zlib", "dep:libz-sys"] + +# libnetcdf +driver_netcdf = ["dep:netcdf-sys", "driver_hdf5"] + +# poppler +#driver_pdf = [] + +# combined +# these are missing at least one dependency +# driver_mvt = [] # requires sqlite + geos +# driver_osm = [] # requires sqlite + libexpat +# driver_zarr = [] # requires liblz4, libxz, libzstd and libblosc +# driver_mbtiles = ["driver_sqlite", "driver_mvt"] # also requires mvt (so geos) +# driver_pmtiles = ["driver_mvt"] # depends on driver_mvt +# driver_csw = ["curl-sys", "driver_gml"] # depends on gml driver +# driver_wfs = ["curl-sys", "driver_gml"] +# driver_pds = ["driver_gml"] + +# unclear +driver_http = [] +driver_arg = [] diff --git a/gdal-src/build.rs b/gdal-src/build.rs new file mode 100644 index 000000000..ccbcea306 --- /dev/null +++ b/gdal-src/build.rs @@ -0,0 +1,372 @@ +use std::path::PathBuf; + +macro_rules! handle_ogr_driver { + ($config: ident, $driver: literal) => { + if cfg!(feature = $driver) { + $config.define(format!("OGR_ENABLE_{}", $driver.to_ascii_uppercase()), "ON"); + } else { + $config.define( + format!("OGR_ENABLE_{}", $driver.to_ascii_uppercase()), + "OFF", + ); + } + }; +} + +macro_rules! handle_gdal_driver { + ($config: ident, $driver: literal) => { + if cfg!(feature = $driver) { + $config.define( + format!("GDAL_ENABLE_{}", $driver.to_ascii_uppercase()), + "ON", + ); + } else { + $config.define( + format!("GDAL_ENABLE_{}", $driver.to_ascii_uppercase()), + "OFF", + ); + } + }; +} + +fn find_library(lib_name: &str, path: impl Into) -> PathBuf { + let path = path.into(); + if path.join("lib64").join(format!("lib{lib_name}.a")).exists() { + path.join("lib64").join(format!("lib{lib_name}.a")) + } else if path.join("lib").join(format!("lib{lib_name}.a")).exists() { + path.join("lib").join(format!("lib{lib_name}.a")) + } else if path.join("lib").join(format!("{lib_name}.lib")).exists() { + path.join("lib").join(format!("{lib_name}.lib")) + } else { + panic!("{lib_name} not found in {}", path.display()); + } +} + +fn main() { + if cfg!(feature = "nobuild") { + return; + } + // gdal doesn't like non clean builds so we remove any artifact from an older build + // https://github.com/OSGeo/gdal/issues/10125 + // This hopefully does not break all the caching as we don't rerun the build script + // everytime, just if something changed. In that case we will do always a clean build + // and not an incremental rebuild because of the gdal build system seems not to be able + // to handle that well + let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").expect("Set by cargo")); + if out_dir.exists() { + let _ = std::fs::remove_dir_all(&out_dir); + let _ = std::fs::create_dir_all(&out_dir); + } + + let proj_root = + std::path::PathBuf::from(std::env::var("DEP_PROJ_ROOT").expect("set by proj-sys")); + let proj_library = if std::env::var("CARGO_CFG_TARGET_FAMILY").as_deref() == Ok("windows") { + if proj_root.join("lib").join("proj_d.lib").exists() { + proj_root.join("lib").join("proj_d.lib") + } else { + proj_root.join("lib").join("proj.lib") + } + } else { + find_library("proj", &proj_root) + }; + let proj_include = proj_root.join("include"); + + let mut config = cmake::Config::new("source"); + + config + .define("GDAL_BUILD_OPTIONAL_DRIVERS", "OFF") + .define("OGR_BUILD_OPTIONAL_DRIVERS", "OFF") + .define("GDAL_USE_INTERNAL_LIBS", "ON") + .define("GDAL_USE_EXTERNAL_LIBS", "OFF") + .define("BUILD_SHARED_LIBS", "OFF") + .define("BUILD_STATIC_LIBS", "ON") + .define("BUILD_APPS", "OFF") + .define("BUILD_DOCS", "OFF") + .define("BUILD_TESTING", "OFF") + .define("BUILD_GMOCK", "OFF") + .define("GDAL_FIND_PACKAGE_PROJ_MODE", "MODULE") + .define("PROJ_INCLUDE_DIR", print_path(&proj_include)) + .define("PROJ_LIBRARY", print_path(&proj_library)) + .define("ACCEPT_MISSING_LINUX_FS_HEADER", "ON"); + // enable the gpkg driver + + // simple drivers without external dependencies + handle_ogr_driver!(config, "driver_avc"); + handle_ogr_driver!(config, "driver_cad"); + handle_ogr_driver!(config, "driver_csv"); + handle_ogr_driver!(config, "driver_dgn"); + handle_ogr_driver!(config, "driver_dxf"); + handle_ogr_driver!(config, "driver_edigeo"); + handle_ogr_driver!(config, "driver_flatgeobuf"); + handle_ogr_driver!(config, "driver_geoconcept"); + handle_ogr_driver!(config, "driver_geojson"); + handle_ogr_driver!(config, "driver_gmt"); + handle_ogr_driver!(config, "driver_gtfs"); + handle_ogr_driver!(config, "driver_jsonfg"); + handle_ogr_driver!(config, "driver_mapml"); + handle_ogr_driver!(config, "driver_openfilegdb"); + handle_ogr_driver!(config, "driver_pgdump"); + handle_ogr_driver!(config, "driver_ntf"); + handle_ogr_driver!(config, "driver_s57"); + handle_ogr_driver!(config, "driver_selafin"); + handle_ogr_driver!(config, "driver_shape"); + handle_ogr_driver!(config, "driver_sxf"); + handle_ogr_driver!(config, "driver_tab"); + handle_ogr_driver!(config, "driver_tiger"); + handle_ogr_driver!(config, "driver_vdv"); + handle_ogr_driver!(config, "driver_wasp"); + handle_ogr_driver!(config, "driver_idrisi"); + handle_ogr_driver!(config, "driver_sdts"); + handle_ogr_driver!(config, "driver_vrt"); + handle_ogr_driver!(config, "driver_mem"); + + handle_gdal_driver!(config, "driver_aaigrid"); + handle_gdal_driver!(config, "driver_adrg"); + handle_gdal_driver!(config, "driver_aigrid"); + handle_gdal_driver!(config, "driver_airsar"); + handle_gdal_driver!(config, "driver_blx"); + handle_gdal_driver!(config, "driver_bmp"); + handle_gdal_driver!(config, "driver_bsb"); + handle_gdal_driver!(config, "driver_cals"); + handle_gdal_driver!(config, "driver_ceos"); + handle_gdal_driver!(config, "driver_coasp"); + handle_gdal_driver!(config, "driver_cosar"); + handle_gdal_driver!(config, "driver_ctg"); + handle_gdal_driver!(config, "driver_dimap"); + handle_gdal_driver!(config, "driver_dted"); + handle_gdal_driver!(config, "driver_elas"); + handle_gdal_driver!(config, "driver_envisat"); + handle_gdal_driver!(config, "driver_ers"); + handle_gdal_driver!(config, "driver_fit"); + handle_gdal_driver!(config, "driver_gff"); + handle_gdal_driver!(config, "driver_gif"); + handle_gdal_driver!(config, "driver_grib"); + handle_gdal_driver!(config, "driver_gsg"); + handle_gdal_driver!(config, "driver_gtiff"); + handle_gdal_driver!(config, "driver_gxf"); + handle_gdal_driver!(config, "driver_hf2"); + handle_gdal_driver!(config, "driver_hfa"); + handle_gdal_driver!(config, "driver_ilwis"); + handle_gdal_driver!(config, "driver_iris"); + handle_gdal_driver!(config, "driver_jaxapalsar"); + handle_gdal_driver!(config, "driver_jdem"); + handle_gdal_driver!(config, "driver_jpeg"); + handle_gdal_driver!(config, "driver_kmlsuperoverlay"); + handle_gdal_driver!(config, "driver_l1b"); + handle_gdal_driver!(config, "driver_leveller"); + handle_gdal_driver!(config, "driver_map"); + handle_gdal_driver!(config, "driver_mrf"); + handle_gdal_driver!(config, "driver_msgn"); + handle_gdal_driver!(config, "driver_ngsgeoid"); + handle_gdal_driver!(config, "driver_nift"); + handle_gdal_driver!(config, "driver_northwood"); + handle_gdal_driver!(config, "driver_ozi"); + handle_gdal_driver!(config, "driver_pcidsk"); + handle_gdal_driver!(config, "driver_pcraster"); + handle_gdal_driver!(config, "driver_png"); + handle_gdal_driver!(config, "driver_prf"); + handle_gdal_driver!(config, "driver_r"); + handle_gdal_driver!(config, "driver_raw"); + handle_gdal_driver!(config, "driver_rik"); + handle_gdal_driver!(config, "driver_rmf"); + handle_gdal_driver!(config, "driver_rs2"); + handle_gdal_driver!(config, "driver_safe"); + handle_gdal_driver!(config, "driver_saga"); + handle_gdal_driver!(config, "driver_sar_ceos"); + handle_gdal_driver!(config, "driver_sentinel2"); + handle_gdal_driver!(config, "driver_sgi"); + handle_gdal_driver!(config, "driver_sigdem"); + handle_gdal_driver!(config, "driver_srtmhgt"); + handle_gdal_driver!(config, "driver_stacit"); + handle_gdal_driver!(config, "driver_stacta"); + handle_gdal_driver!(config, "driver_terragen"); + handle_gdal_driver!(config, "driver_tga"); + handle_gdal_driver!(config, "driver_til"); + handle_gdal_driver!(config, "driver_tsx"); + handle_gdal_driver!(config, "driver_usgsdem"); + handle_gdal_driver!(config, "driver_xpm"); + handle_gdal_driver!(config, "driver_xyz"); + handle_gdal_driver!(config, "driver_zmap"); + handle_gdal_driver!(config, "driver_idrisi"); + handle_gdal_driver!(config, "driver_pds"); + handle_gdal_driver!(config, "driver_sdts"); + handle_gdal_driver!(config, "driver_vrt"); + handle_gdal_driver!(config, "driver_mem"); + + if cfg!(feature = "driver_sqlite") { + let sqlite3_include_dir = + std::env::var("DEP_SQLITE3_INCLUDE").expect("This is set by libsqlite3-sys"); + let sqlite3_lib_dir = std::env::var("DEP_SQLITE3_LIB_DIR").expect("set by libsqlite3-sys"); + let mut sqlite3_lib = PathBuf::from(sqlite3_lib_dir); + sqlite3_lib.push("libsqlite3.a"); + let sqlite3_include_dir = PathBuf::from(sqlite3_include_dir); + + config + .define("GDAL_USE_SQLITE3", "ON") + .define("SQLite3_INCLUDE_DIR", print_path(&sqlite3_include_dir)) + .define("SQLite3_LIBRARY", print_path(&sqlite3_lib)) + .define("OGR_ENABLE_DRIVER_SQLITE", "ON"); + } else { + config.define("GDAL_USE_SQLITE3", "OFF"); + } + // these drivers depend on sqlite + handle_ogr_driver!(config, "driver_gpkg"); + handle_ogr_driver!(config, "driver_vfk"); + + if cfg!(feature = "driver_hdf5") { + let hdf5_dir = std::env::var("DEP_HDF5_ROOT").expect("This is set by hdf5-sys"); + let hdf5_lib = std::env::var("DEP_HDF5_LIBRARY").expect("This is set by hdf5-sys"); + let hdf5_lib_dir = find_library(&hdf5_lib, &hdf5_dir); + let mut hdf5_cc = PathBuf::from(&hdf5_dir); + hdf5_cc.push("bin"); + hdf5_cc.push("h5cc"); + let hdf5_include = std::env::var("DEP_HDF5_INCLUDE").expect("This is set by hdf5-sys"); + let hdf5_include = PathBuf::from(&hdf5_include); + config + .define("GDAL_USE_HDF5", "ON") + .define("HDF5_C_COMPILER_EXECUTABLE", print_path(&hdf5_cc)) + .define("HDF5_C_INCLUDE_DIR", print_path(&hdf5_include)) + .define("HDF5_hdf5_LIBRARY_DEBUG", print_path(&hdf5_lib_dir)) + .define("HDF5_hdf5_LIBRARY_RELEASE", print_path(&hdf5_lib_dir)) + .define("GDAL_ENABLE_DRIVER_HDF5", "ON") + .define("HDF5_USE_STATIC_LIBRARIES", "ON"); + } else { + config.define("GDAL_USE_HDF5", "OFF"); + } + + if cfg!(feature = "driver_netcdf") { + let netcdf_include = + std::env::var("DEP_NETCDF_INCLUDEDIR").expect("This is set by netcdf-sys"); + let netcdf_root = format!("{netcdf_include}/.."); + + let netcdf_include = PathBuf::from(netcdf_include); + let netcdf_root = PathBuf::from(netcdf_root); + let netcdf_lib = find_library("netcdf", &netcdf_root); + + config + .define("GDAL_USE_NETCDF", "ON") + .define("NETCDF_INCLUDE_DIR", print_path(&netcdf_include)) + .define("NETCDF_LIBRARY", print_path(&netcdf_lib)) + .define("GDAL_ENABLE_DRIVER_NETCDF", "ON"); + } else { + config.define("GDAL_USE_NETCDF", "OFF"); + } + + if cfg!(feature = "curl-sys") { + let curl_root = std::env::var("DEP_CURL_ROOT").expect("set from curl-sys"); + let mut curl_include = PathBuf::from(&curl_root); + curl_include.push("include"); + let mut curl_lib = PathBuf::from(curl_root); + curl_lib.push("build"); + curl_lib.push("libcurl.a"); + config + .define("GDAL_USE_CURL", "ON") + .define("CURL_INCLUDE_DIR", print_path(&curl_include)) + .define("CURL_LIBRARY_DEBUG", print_path(&curl_lib)) + .define("CURL_LIBRARY_RELEASE", print_path(&curl_lib)) + .define("CURL_USE_STATIC_LIBS", "ON"); + } else { + config.define("GDAL_USE_CURL", "OFF"); + } + + handle_ogr_driver!(config, "driver_amigocloud"); + handle_ogr_driver!(config, "driver_carto"); + handle_ogr_driver!(config, "driver_daas"); + handle_ogr_driver!(config, "driver_eeda"); + handle_ogr_driver!(config, "driver_elastic"); + handle_ogr_driver!(config, "driver_ngw"); + handle_gdal_driver!(config, "driver_ogcapi"); + handle_gdal_driver!(config, "driver_plmosaic"); + handle_gdal_driver!(config, "driver_wcs"); + handle_gdal_driver!(config, "driver_wms"); + handle_gdal_driver!(config, "driver_wmts"); + + if cfg!(feature = "driver_pg") { + let pq_include = std::env::var("DEP_PQ_SYS_SRC_INCLUDE").expect("this is set by pq-src"); + let pq_lib = std::env::var("DEP_PQ_SYS_SRC_LIB_DIR").expect("this is set by pq-src"); + let pq_lib_path = std::path::PathBuf::from(&pq_lib); + println!("cargo:rustc-link-search=native={}", pq_lib_path.display()); + let pq_lib_path = if pq_lib_path.join("libpq.a").exists() { + pq_lib_path.join("libpq.a") + } else if pq_lib_path.join("pq.lib").exists() { + pq_lib_path.join("pq.lib") + } else { + panic!("Libpq not found in {pq_lib}"); + }; + let pq_include = PathBuf::from(pq_include); + println!("cargo:rustc-link-lib=static=pq"); + config + .define("GDAL_USE_POSTGRESQL", "ON") + .define("PostgreSQL_INCLUDE_DIR", print_path(&pq_include)) + .define("PostgreSQL_LIBRARY_DEBUG", print_path(&pq_lib_path)) + .define("PostgreSQL_LIBRARY_RELEASE", print_path(&pq_lib_path)) + .define("OGR_ENABLE_DRIVER_PG", "ON"); + } else { + config.define("GDAL_USE_POSTGRESQL", "OFF"); + } + handle_gdal_driver!(config, "driver_postgis_raster"); + + if cfg!(feature = "geos") { + config.define("GDAL_USE_GEOS", "ON"); + } else { + config.define("GDAL_USE_GEOS", "OFF"); + } + + if cfg!(feature = "geos_static") { + let geos_root = std::env::var("DEP_GEOSSRC_ROOT").expect("this is set by geos-src"); + let mut geos_include = PathBuf::from(&geos_root); + geos_include.push("include"); + config.define("GEOS_INCLUDE_DIR", print_path(&geos_include)); + let lib_path = find_library("geos", geos_root); + config.define("GEOS_LIBRARY", print_path(&lib_path)); + } + + if cfg!(target_env = "msvc") { + // otherwise there are linking issues + // because rust always links the + // MSVC release runtime + config.profile("Release"); + // see + // https://github.com/OSGeo/PROJ/commit/6e9b324ab7bf5909df7e68409e060282db14fa54#diff-af8fe2f9d33a9c3408ff7683bfebd1e2334b4506f559add92406be3e150268fb + config.cxxflag("-DPROJ_DLL="); + // that windows library is somehow required + println!("cargo:rustc-link-lib=Wbemuuid"); + } + + let res = config.build(); + + // sometimes it's lib and sometimes it's lib64 and sometimes `build/lib` + let lib_dir = res.join("lib64"); + println!( + "cargo:rustc-link-search=native={}", + lib_dir.to_str().unwrap() + ); + let lib_dir = res.join("lib"); + println!( + "cargo:rustc-link-search=native={}", + lib_dir.to_str().unwrap() + ); + let lib_dir = res.join("build").join("lib"); + println!( + "cargo:rustc-link-search=native={}", + lib_dir.to_str().unwrap() + ); + + //gdal likes to create gdal_d when configured as debug and on MSVC, so link to that one if it exists + if res.join("lib").join("gdald.lib").exists() { + println!("cargo:rustc-link-lib=static=gdald"); + } else { + println!("cargo:rustc-link-lib=static=gdal"); + } +} + +// cmake sometimes does not like windows paths like `c:\\whatever\folder` +// it seems to tread `\` as escape sequence in some cases, therefore +// we rewrite the path here to always use `/` as seperator +// https://github.com/OSGeo/gdal/issues/9935 +fn print_path(path: &std::path::Path) -> String { + path.components() + .map(|c| c.as_os_str().to_str().unwrap()) + .collect::>() + .join("/") +} diff --git a/gdal-src/source b/gdal-src/source new file mode 160000 index 000000000..01ac70772 --- /dev/null +++ b/gdal-src/source @@ -0,0 +1 @@ +Subproject commit 01ac70772b997ab87d2312e2036eb66faf6b0508 diff --git a/gdal-src/src/lib.rs b/gdal-src/src/lib.rs new file mode 100644 index 000000000..d8a40ced8 --- /dev/null +++ b/gdal-src/src/lib.rs @@ -0,0 +1,15 @@ +#[cfg(feature = "curl-sys")] +extern crate curl_sys; +#[cfg(feature = "geos")] +extern crate geos_sys; +#[cfg(feature = "driver_hdf5")] +extern crate hdf5_sys; +#[cfg(feature = "driver_sqlite")] +extern crate libsqlite3_sys; +#[cfg(feature = "driver_netcdf")] +extern crate netcdf_sys; +#[cfg(feature = "driver_pg")] +extern crate pq_src; + +extern crate link_cplusplus; +extern crate proj_sys; diff --git a/gdal-sys/Cargo.toml b/gdal-sys/Cargo.toml index 3fef2ba8c..e76cb77d4 100644 --- a/gdal-sys/Cargo.toml +++ b/gdal-sys/Cargo.toml @@ -12,8 +12,14 @@ links="gdal" [dependencies] libc = "0.2" +gdal-src = { path = "../gdal-src/", optional = true } [build-dependencies] bindgen = { version = "0.69", optional = true } pkg-config = "0.3" semver = "1.0" + + +[features] +default = [] +bundled = ["dep:gdal-src"] diff --git a/gdal-sys/build.rs b/gdal-sys/build.rs index b4e20e62d..f6e516488 100644 --- a/gdal-sys/build.rs +++ b/gdal-sys/build.rs @@ -68,7 +68,7 @@ fn main() { // Hardcode a prebuilt binding version while generating docs. // Otherwise docs.rs will explode due to not actually having libgdal installed. - if std::env::var("DOCS_RS").is_ok() { + if std::env::var("DOCS_RS").is_ok() || cfg!(feature = "bundled") { let version = Version::parse("3.9.0").expect("invalid version for docs.rs"); println!( "cargo:rustc-cfg=gdal_sys_{}_{}_{}", diff --git a/gdal-sys/src/lib.rs b/gdal-sys/src/lib.rs index 98d15c1b7..f8c611e1a 100644 --- a/gdal-sys/src/lib.rs +++ b/gdal-sys/src/lib.rs @@ -4,4 +4,7 @@ #![allow(clippy::upper_case_acronyms)] #![allow(rustdoc::bare_urls)] +#[cfg(feature = "bundled")] +extern crate gdal_src; + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/src/programs/raster/mdimtranslate.rs b/src/programs/raster/mdimtranslate.rs index d90bd6363..c5544f49c 100644 --- a/src/programs/raster/mdimtranslate.rs +++ b/src/programs/raster/mdimtranslate.rs @@ -231,7 +231,7 @@ mod tests { use crate::{DriverManager, GdalOpenFlags}; #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] fn test_build_tiff_from_path() { let fixture = "/vsizip/fixtures/cf_nasa_4326.zarr.zip"; @@ -263,7 +263,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] fn test_build_tiff_from_dataset() { let fixture = "/vsizip/fixtures/cf_nasa_4326.zarr.zip"; diff --git a/src/raster/mdarray.rs b/src/raster/mdarray.rs index 594763e95..0039024c9 100644 --- a/src/raster/mdarray.rs +++ b/src/raster/mdarray.rs @@ -848,7 +848,7 @@ mod tests { use crate::{test_utils::TempFixture, Dataset, GdalOpenFlags}; #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_root_group_name() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -866,7 +866,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_array_names() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -888,7 +888,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_n_dimension() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -909,7 +909,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_n_elements() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -930,7 +930,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_dimension_name() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -966,7 +966,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_dimension_size() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -991,7 +991,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_read_data() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -1028,7 +1028,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_1), major_ge_4))] fn test_read_string_array() { // Beware https://github.com/georust/gdal/issues/299 if you want to reuse this @@ -1062,7 +1062,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_datatype() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -1089,7 +1089,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_spatial_ref() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -1115,7 +1115,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_no_data_value() { let fixture = "/vsizip/fixtures/byte_no_cf.zarr.zip"; @@ -1137,7 +1137,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_attributes() { let fixture = "/vsizip/fixtures/cf_nasa_4326.zarr.zip"; @@ -1189,7 +1189,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_unit() { let fixture = "/vsizip/fixtures/cf_nasa_4326.zarr.zip"; @@ -1230,7 +1230,7 @@ mod tests { } #[test] - #[cfg_attr(not(all(major_ge_3, minor_ge_4)), ignore)] + #[cfg_attr(any(not(all(major_ge_3, minor_ge_4)), feature = "gdal-src"), ignore)] #[cfg(any(all(major_is_3, minor_ge_2), major_ge_4))] fn test_stats() { // make a copy to avoid writing the statistics into the original file diff --git a/src/vector/sql.rs b/src/vector/sql.rs index 00f6ca9a7..31f017f06 100644 --- a/src/vector/sql.rs +++ b/src/vector/sql.rs @@ -236,6 +236,7 @@ mod tests { } #[test] + #[cfg_attr(feature = "gdal-src", ignore)] fn test_sql_with_dialect() { let query = "SELECT * FROM roads WHERE highway = 'pedestrian' and NumPoints(GEOMETRY) = 3"; let ds = Dataset::open(fixture("roads.geojson")).unwrap();