diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 680d47194..e00b16ada 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - build: [x86_64, i686, x86_64-musl, mingw, system-curl, x86_64-beta, x86_64-nightly, macos, win64, win32] + build: [x86_64, i686, x86_64-musl, mingw, system-curl, x86_64-beta, x86_64-nightly, macos, win64-static-ssl, win64, win32] include: - build: x86_64 os: ubuntu-latest @@ -67,6 +67,11 @@ jobs: os: windows-latest rust: stable target: x86_64-pc-windows-msvc + - build: win64-static-ssl + os: windows-latest + rust: stable + target: x86_64-pc-windows-msvc + static_ssl: windows steps: - uses: actions/checkout@v1 with: @@ -75,7 +80,29 @@ jobs: run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} shell: bash - run: rustup target add ${{ matrix.target }} - - run: TARGET=${{ matrix.target }} ./ci/run.sh + - name: Compute vcpkg cache entries + if: matrix.static_ssl == 'windows' + id: vcpkg-cache + shell: bash + run: | + echo "path=${{ runner.temp }}/vcpkg-cache" >> "$GITHUB_OUTPUT" + echo "key=vcpkg-${{ matrix.target }}-${{ hashFiles('vcpkg.json') }}-$ImageVersion" >> "$GITHUB_OUTPUT" + - name: Restore vcpkg binary cache (files backend) + if: matrix.static_ssl == 'windows' + uses: actions/cache@v5 + with: + path: ${{ steps.vcpkg-cache.outputs.path }} + key: ${{ steps.vcpkg-cache.outputs.key }} + - name: Install vcpkg dependencies (for windows-static-ssl) + if: matrix.static_ssl == 'windows' + shell: bash + env: + VCPKG_BINARY_SOURCES: "clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite" + run: | + vcpkg install --triplet=x64-windows-static-md \ + --x-install-root="$VCPKG_INSTALLATION_ROOT/installed" + echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> "$GITHUB_ENV" + - run: TARGET=${{ matrix.target }} STATIC_SSL=${{ matrix.static_ssl }} ./ci/run.sh if: matrix.os != 'ubuntu-latest' name: Run non-docker tests shell: bash diff --git a/ci/run.sh b/ci/run.sh index 0abfb5fd6..6e2afaa8a 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -16,6 +16,16 @@ if [ "$TARGET" = "x86_64-unknown-linux-musl" ]; then features="--features static-ssl" fi +# `windows-static-ssl` (curl + OpenSSL on Windows) needs vcpkg-installed +# OpenSSL and `VCPKG_ROOT` set for vcpkg-rs's discovery. +if [ "$STATIC_SSL" = "windows" ]; then + features="--features windows-static-ssl --no-default-features" + # Enable the Windows native CA store for the test suite so doctests that + # make HTTPS requests can verify server certificates. Not baked into + # build.rs to avoid forcing the default on library consumers. + export CFLAGS="-DCURL_CA_NATIVE" +fi + cargo test --target $TARGET --no-run $features # First test with no extra protocols enabled. cargo test --target $TARGET --no-run --features static-curl $features @@ -28,7 +38,11 @@ cargo test --target $TARGET --no-run --features static-curl $features # Compiling on i686-windows requires nasm to be installed (other platforms # have pre-compiled object files), which is just slightly too much # inconvenience for me. -if [ "$TARGET" != "x86_64-pc-windows-gnu" ] && [ "$TARGET" != "i686-pc-windows-msvc" ] +# +# Note also that rustls silently wins over windows-static-ssl in curl-sys's +# build.rs else-if, so we skip this on the win64-static-ssl entry and keep +# actually exercising windows-static-ssl in the tests below. +if [ "$TARGET" != "x86_64-pc-windows-gnu" ] && [ "$TARGET" != "i686-pc-windows-msvc" ] && [ "$STATIC_SSL" != "windows" ] then cargo test --target $TARGET --no-run --features rustls,static-curl $features fi diff --git a/curl-sys/build.rs b/curl-sys/build.rs index 7d028d410..bf62c8d08 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -360,17 +360,39 @@ fn main() { if windows { cfg.define("USE_OPENSSL", None) .file("curl/lib/vtls/openssl.c"); - // We need both openssl and zlib - // Those can be installed with - // ```shell - // git clone https://github.com/microsoft/vcpkg - // cd vcpkg - // ./bootstrap-vcpkg.bat -disableMetrics - // ./vcpkg.exe integrate install - // ./vcpkg.exe install openssl:x64-windows-static-md - // ``` - #[cfg(target_env = "msvc")] - vcpkg::Config::new().find_package("openssl").ok(); + // Probe `_OPENSSL_DIR` / `OPENSSL_DIR` before falling + // back to vcpkg. Matches openssl-sys's env-lookup convention so + // a single env var can point both `openssl-sys` and `curl-sys` + // at the same OpenSSL install. + if let Some(dir) = openssl_dir_from_env(&target) { + cfg.include(dir.join("include")); + println!( + "cargo:rustc-link-search=native={}", + dir.join("lib").display() + ); + println!("cargo:rustc-link-lib=libssl"); + println!("cargo:rustc-link-lib=libcrypto"); + } else { + // vcpkg openssl fallback. Install with: + // ```shell + // git clone https://github.com/microsoft/vcpkg + // cd vcpkg + // ./bootstrap-vcpkg.bat -disableMetrics + // ./vcpkg.exe integrate install + // ./vcpkg.exe install openssl:x64-windows-static-md + // ``` + // `find_package` emits `rustc-link-search` + `rustc-link-lib` + // for the linker, but the returned `Library`'s `include_paths` + // still have to be threaded into `cc-rs` explicitly. + #[cfg(target_env = "msvc")] + if let Ok(lib) = vcpkg::Config::new().find_package("openssl") { + for path in &lib.include_paths { + cfg.include(path); + } + } + } + // OpenSSL static libs reference MessageBoxA (OPENSSL_showfatal). + println!("cargo:rustc-link-lib=user32"); #[cfg(target_env = "msvc")] vcpkg::Config::new().find_package("zlib").ok(); } else { @@ -529,6 +551,20 @@ fn main() { } } +/// Probe `_OPENSSL_DIR` first, then plain `OPENSSL_DIR`, matching +/// openssl-sys's env-lookup convention (`build/main.rs::env`). Always emits +/// `rerun-if-env-changed` for both names so the build is invalidated when +/// the env changes, even when neither is set at build time. +fn openssl_dir_from_env(target: &str) -> Option { + let prefix = target.to_uppercase().replace('-', "_"); + let prefixed = format!("{prefix}_OPENSSL_DIR"); + println!("cargo:rerun-if-env-changed={prefixed}"); + println!("cargo:rerun-if-env-changed=OPENSSL_DIR"); + env::var_os(&prefixed) + .or_else(|| env::var_os("OPENSSL_DIR")) + .map(PathBuf::from) +} + #[cfg(not(target_env = "msvc"))] fn try_vcpkg() -> bool { false diff --git a/src/lib.rs b/src/lib.rs index 2965e2bed..14211bdeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,13 @@ #![deny(missing_docs, missing_debug_implementations)] #![doc(html_root_url = "https://docs.rs/curl/0.4")] +// `ssl` (Schannel on Windows) and `windows-static-ssl` (OpenSSL) are mutually +// exclusive TLS backends for libcurl. +// Gated on `not(doc)` so that `cargo doc --all-features` still generates a +// union of feature-conditional items. +#[cfg(all(feature = "ssl", feature = "windows-static-ssl", not(doc)))] +compile_error!("`ssl` and `windows-static-ssl` are mutually exclusive TLS backends"); + use std::ffi::CStr; use std::str; use std::sync::Once; diff --git a/systest/Cargo.toml b/systest/Cargo.toml index a59386ed0..d77e5a62b 100644 --- a/systest/Cargo.toml +++ b/systest/Cargo.toml @@ -16,3 +16,4 @@ ctest2 = "0.4" [features] static-ssl = ['curl-sys/static-ssl'] +windows-static-ssl = ['curl-sys/windows-static-ssl'] diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 000000000..9b403b126 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,13 @@ +{ + "builtin-baseline": "960d40ee7351cca3fc648e9d38993b59f01dfd2b", + "dependencies": [ + "openssl" + ], + "overrides": [ + { + "name": "openssl", + "version": "3.6.4", + "port-version": 1 + } + ] +}