diff --git a/.cargo/config.toml b/.cargo/config.toml index fd663c7cd6..28367fa6ef 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -6,3 +6,8 @@ linker = "rust-lld" [target.i686-pc-windows-msvc] linker = "rust-lld" + +[alias] +ci-test = "hack test --package ext-php-rs --package tests --feature-powerset --clean-per-run --exclude-features=link-php" +ci-test-link = "hack test --package ext-php-rs --feature-powerset --clean-per-run --features=link-php" +ci-test-other = "hack test --workspace --exclude ext-php-rs --exclude tests --feature-powerset" diff --git a/.github/actions/embed/Dockerfile b/.github/actions/embed/Dockerfile deleted file mode 100644 index 13ee7c7250..0000000000 --- a/.github/actions/embed/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM php:8.2-bullseye - -WORKDIR /tmp - -RUN apt update -y && apt upgrade -y -RUN apt install lsb-release wget gnupg software-properties-common -y -RUN bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" - -ENV RUSTUP_HOME=/rust -ENV CARGO_HOME=/cargo -ENV PATH=/cargo/bin:/rust/bin:$PATH - -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path - -ENTRYPOINT [ "/cargo/bin/cargo", "test", "--all", "--release", "--all-features", "--no-fail-fast" ] diff --git a/.github/actions/embed/action.yml b/.github/actions/embed/action.yml deleted file mode 100644 index c99ebf5024..0000000000 --- a/.github/actions/embed/action.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: 'PHP Embed and Rust' -description: 'Builds the crate after installing the latest PHP with php embed and stable Rust.' -runs: - using: 'docker' - image: 'Dockerfile' diff --git a/.github/actions/zts/Dockerfile b/.github/actions/zts/Dockerfile deleted file mode 100644 index e23f55bba5..0000000000 --- a/.github/actions/zts/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM php:8.2-zts-bullseye - -WORKDIR /tmp - -RUN apt update -y && apt upgrade -y -RUN apt install lsb-release wget gnupg software-properties-common -y -RUN bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" - -ENV RUSTUP_HOME=/rust -ENV CARGO_HOME=/cargo -ENV PATH=/cargo/bin:/rust/bin:$PATH - -RUN (curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly --no-modify-path) && rustup default nightly - -ENTRYPOINT [ "/cargo/bin/cargo", "build", "--all", "--release" ] \ No newline at end of file diff --git a/.github/actions/zts/action.yml b/.github/actions/zts/action.yml deleted file mode 100644 index 9cd1bd1125..0000000000 --- a/.github/actions/zts/action.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: 'PHP ZTS and Rust' -description: 'Builds the crate after installing the latest PHP with ZTS and stable Rust.' -runs: - using: 'docker' - image: 'Dockerfile' \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64da56dbba..d8ab1d8754 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,31 +76,29 @@ jobs: env: DOCS_RS: "" run: cargo clean && cargo build - build: - name: Build and Test + + test: + name: Test runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] php: ["8.0", "8.1", "8.2", "8.3", "8.4"] rust: [stable, nightly] - clang: ["15", "17"] phpts: [ts, nts] exclude: - # ext-php-rs requires nightly Rust when on Windows. - - os: windows-latest - rust: stable - # setup-php doesn't support thread safe PHP on Linux and macOS. - - os: macos-latest - phpts: ts - - os: ubuntu-latest - phpts: ts - os: macos-latest - clang: "17" + rust: nightly - os: ubuntu-latest - clang: "15" + rust: nightly - os: windows-latest - clang: "15" + rust: stable + include: + - os: ubuntu-latest + rust: nightly + phpts: nts + php: "8.4" env: CARGO_TERM_COLOR: always steps: @@ -118,63 +116,87 @@ jobs: with: toolchain: ${{ matrix.rust }} components: rustfmt, clippy - - run: rustup show - - name: Cache cargo dependencies - uses: Swatinem/rust-cache@v2 - # Uncomment the following if statement if caching nightly deps - # ends up causing too much cache invalidation. - # if: matrix.rust == 'stable' + - name: Install LLVM and Clang + uses: jhchundev/install-llvm-action@v1.0.0 + if: "contains(matrix.os, 'windows')" with: - # increment this manually to force cache eviction - prefix-key: ${{ env.RUST_CACHE_PREFIX }} - # LLVM & Clang - - name: Cache LLVM and Clang - id: cache-llvm - uses: actions/cache@v4 - # Macos build doesn't work with clang < 18. As a build for version 18 is not available, we skip the setup - if: "!contains(matrix.os, 'windows') && !contains(matrix.os, 'macos')" - with: - path: ${{ runner.temp }}/llvm-${{ matrix.clang }} - key: ${{ matrix.os }}-llvm-${{ matrix.clang }} - - name: Setup LLVM & Clang - id: clang - uses: KyleMayes/install-llvm-action@v2 - # Macos build doesn't work with clang < 18. As a build for version 18 is not available, we skip the setup - if: "!contains(matrix.os, 'windows') && !contains(matrix.os, 'macos')" + version: "15.0" + - name: Install cargo-hack + uses: taiki-e/install-action@v2 with: - version: ${{ matrix.clang }} - directory: ${{ runner.temp }}/llvm-${{ matrix.clang }} - cached: ${{ steps.cache-llvm.outputs.cache-hit }} - - name: Configure Clang - # Macos build doesn't work with clang < 18. As a build for version 18 is not available, we skip the setup - if: "!contains(matrix.os, 'windows') && !contains(matrix.os, 'macos')" - run: | - echo "LIBCLANG_PATH=${{ runner.temp }}/llvm-${{ matrix.clang }}/lib" >> $GITHUB_ENV - echo "LLVM_VERSION=${{ steps.clang.outputs.version }}" >> $GITHUB_ENV - echo "LLVM_CONFIG_PATH=${{ runner.temp }}/llvm-${{ matrix.clang }}/bin/llvm-config" >> $GITHUB_ENV - # Build - - name: Build + tool: cargo-hack + # show clang version if on windows + - run: clang --version + if: "contains(matrix.os, 'windows')" + - name: Test env: EXT_PHP_RS_TEST: "" - run: cargo build --release --features closure,anyhow --all - # Test - - name: Test inline examples - # Macos fails on unstable rust. We skip the inline examples test for now. - if: "!(contains(matrix.os, 'macos') && matrix.rust == 'nightly')" - run: cargo test --release --all --features closure,anyhow --no-fail-fast - build-zts: - name: Build with ZTS - runs-on: ubuntu-latest + run: cargo ci-test + + test-link: + name: Test link php + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + php: ["8.2", "8.3", "8.4"] + phpts: [ts, nts] + env: + CARGO_TERM_COLOR: always steps: - name: Checkout code uses: actions/checkout@v4 - - name: Build - uses: ./.github/actions/zts - test-embed: - name: Test with embed - runs-on: ubuntu-latest + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + env: + phpts: ${{ matrix.phpts }} + debug: true + - name: Setup Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: rustfmt, clippy + - run: rustup show + - name: Install cargo-hack + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack + - name: Test + env: + EXT_PHP_RS_TEST: "" + run: cargo ci-test-link + + test-others: + name: Test others + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + rust: [stable, nightly] + env: + CARGO_TERM_COLOR: always steps: - name: Checkout code uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.4 + - name: Setup Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + components: rustfmt, clippy + - name: Install cargo-hack + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack + # Test - name: Test - uses: ./.github/actions/embed + env: + EXT_PHP_RS_TEST: "" + run: cargo ci-test-other diff --git a/Cargo.toml b/Cargo.toml index 56cd55125a..dbe3f14e33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ zip = "2.2" [features] closure = [] -embed = [] +link-php = [] [workspace] members = [ diff --git a/build.rs b/build.rs index 4249964936..bb57d3bc3e 100644 --- a/build.rs +++ b/build.rs @@ -22,6 +22,11 @@ use impl_::Provider; const MIN_PHP_API_VER: u32 = 20200930; const MAX_PHP_API_VER: u32 = 20240924; +const PHP_81_API_VER: u32 = 20210902; +const PHP_82_API_VER: u32 = 20220829; +const PHP_83_API_VER: u32 = 20230831; +const PHP_84_API_VER: u32 = 20240924; + /// Provides information about the PHP installation. pub trait PHPProvider<'a>: Sized { /// Create a new PHP provider. @@ -30,6 +35,9 @@ pub trait PHPProvider<'a>: Sized { /// Retrieve a list of absolute include paths. fn get_includes(&self) -> Result>; + /// Retrieve a list of compiled sapis + fn get_sapis(&self) -> Result>; + /// Retrieve a list of macro definitions to pass to the compiler. fn get_defines(&self) -> Result>; @@ -162,7 +170,6 @@ fn build_wrapper(defines: &[(&str, &str)], includes: &[PathBuf]) -> Result<()> { Ok(()) } -#[cfg(feature = "embed")] /// Builds the embed library. fn build_embed(defines: &[(&str, &str)], includes: &[PathBuf]) -> Result<()> { let mut build = cc::Build::new(); @@ -178,11 +185,14 @@ fn build_embed(defines: &[(&str, &str)], includes: &[PathBuf]) -> Result<()> { } /// Generates bindings to the Zend API. -fn generate_bindings(defines: &[(&str, &str)], includes: &[PathBuf]) -> Result { +fn generate_bindings( + defines: &[(&str, &str)], + includes: &[PathBuf], + has_embed: bool, +) -> Result { let mut bindgen = bindgen::Builder::default(); - #[cfg(feature = "embed")] - { + if has_embed { bindgen = bindgen.header("src/embed/embed.h"); } @@ -245,16 +255,9 @@ fn check_php_version(info: &PHPInfo) -> Result<()> { // // The PHP version cfg flags should also stack - if you compile on PHP 8.2 you // should get both the `php81` and `php82` flags. - const PHP_81_API_VER: u32 = 20210902; - - const PHP_82_API_VER: u32 = 20220829; - - const PHP_83_API_VER: u32 = 20230831; - - const PHP_84_API_VER: u32 = 20240924; println!( - "cargo::rustc-check-cfg=cfg(php80, php81, php82, php83, php84, php_zts, php_debug, docs)" + "cargo::rustc-check-cfg=cfg(php80, php81, php82, php83, php84, php_zts, php_debug, php_embed, docs)" ); println!("cargo:rustc-cfg=php80"); @@ -317,16 +320,21 @@ fn main() -> Result<()> { let info = PHPInfo::get(&php)?; let provider = Provider::new(&info)?; + let sapis = provider.get_sapis()?; let includes = provider.get_includes()?; let defines = provider.get_defines()?; + let has_embed = sapis.contains(&"embed".to_string()); check_php_version(&info)?; build_wrapper(&defines, &includes)?; - #[cfg(feature = "embed")] - build_embed(&defines, &includes)?; + if has_embed { + println!("cargo:rustc-cfg=php_embed"); + + build_embed(&defines, &includes)?; + } - let bindings = generate_bindings(&defines, &includes)?; + let bindings = generate_bindings(&defines, &includes, has_embed)?; let out_file = File::create(&out_path).context("Failed to open output bindings file for writing")?; diff --git a/src/builders/function.rs b/src/builders/function.rs index 6a4f402dd0..02d734bd3b 100644 --- a/src/builders/function.rs +++ b/src/builders/function.rs @@ -9,16 +9,16 @@ use crate::{ use std::{ffi::CString, mem, ptr}; /// Function representation in Rust. -#[cfg(not(windows))] +#[cfg(not(target_family = "windows"))] pub type FunctionHandler = extern "C" fn(execute_data: &mut ExecuteData, retval: &mut Zval); -#[cfg(windows)] +#[cfg(target_family = "windows")] pub type FunctionHandler = extern "vectorcall" fn(execute_data: &mut ExecuteData, retval: &mut Zval); /// Function representation in Rust using pointers. -#[cfg(not(windows))] +#[cfg(not(target_family = "windows"))] type FunctionPointerHandler = extern "C" fn(execute_data: *mut ExecuteData, retval: *mut Zval); -#[cfg(windows)] +#[cfg(target_family = "windows")] type FunctionPointerHandler = extern "vectorcall" fn(execute_data: *mut ExecuteData, retval: *mut Zval); diff --git a/src/builders/mod.rs b/src/builders/mod.rs index 070d717314..5132582f92 100644 --- a/src/builders/mod.rs +++ b/src/builders/mod.rs @@ -4,11 +4,11 @@ mod class; mod function; mod module; -#[cfg(feature = "embed")] +#[cfg(php_embed)] mod sapi; pub use class::ClassBuilder; pub use function::FunctionBuilder; pub use module::{ModuleBuilder, ModuleStartup}; -#[cfg(feature = "embed")] +#[cfg(php_embed)] pub use sapi::SapiBuilder; diff --git a/src/describe/stub.rs b/src/describe/stub.rs index 46daeb5596..aa65201628 100644 --- a/src/describe/stub.rs +++ b/src/describe/stub.rs @@ -320,9 +320,9 @@ impl ToStub for Constant { } } -#[cfg(windows)] +#[cfg(target_family = "windows")] const NEW_LINE_SEPARATOR: &str = "\r\n"; -#[cfg(not(windows))] +#[cfg(not(target_family = "windows"))] const NEW_LINE_SEPARATOR: &str = "\n"; /// Takes a class name and splits the namespace off from the actual class name. diff --git a/src/embed/mod.rs b/src/embed/mod.rs index 0a9cf18c15..f93ab74e7c 100644 --- a/src/embed/mod.rs +++ b/src/embed/mod.rs @@ -59,7 +59,7 @@ impl Embed { /// /// # Example /// - /// ``` + /// ```no_run /// use ext_php_rs::embed::Embed; /// /// Embed::run(|| { @@ -82,8 +82,12 @@ impl Embed { filename: null_mut(), opened_path: null_mut(), type_: 0, + #[cfg(php81)] primary_script: false, + #[cfg(php81)] in_list: false, + #[cfg(not(php81))] + free_filename: false, buf: null_mut(), len: 0, }; @@ -96,8 +100,11 @@ impl Embed { match exec_result { Err(_) => Err(EmbedError::CatchError), + #[cfg(not(php82))] + Ok(1) => Ok(()), + #[cfg(php82)] Ok(true) => Ok(()), - Ok(false) => Err(EmbedError::ExecuteScriptError), + Ok(_) => Err(EmbedError::ExecuteScriptError), } } @@ -116,7 +123,7 @@ impl Embed { /// /// # Example /// - /// ``` + /// ```no_run /// use ext_php_rs::embed::Embed; /// /// Embed::run(|| { @@ -172,7 +179,7 @@ impl Embed { /// /// # Example /// - /// ``` + /// ```no_run /// use ext_php_rs::embed::Embed; /// /// Embed::run(|| { @@ -204,7 +211,7 @@ impl Embed { } } -#[cfg(test)] +#[cfg(all(test, feature = "link-php"))] mod tests { use super::Embed; diff --git a/src/lib.rs b/src/lib.rs index 8af7fdd333..ad1c0fd02d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ pub mod class; pub mod closure; pub mod constant; pub mod describe; -#[cfg(feature = "embed")] +#[cfg(php_embed)] pub mod embed; #[doc(hidden)] pub mod internal; diff --git a/src/types/iterator.rs b/src/types/iterator.rs index b6591ab348..0e13dd2161 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -182,8 +182,7 @@ impl<'a> FromZvalMut<'a> for &'a mut ZendIterator { } } -#[cfg(test)] -#[cfg(feature = "embed")] +#[cfg(all(test, php_embed, feature = "link-php"))] mod tests { use crate::embed::Embed; diff --git a/src/types/string.rs b/src/types/string.rs index 84d93a164b..6321701795 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -455,8 +455,7 @@ impl<'a> FromZval<'a> for &'a str { } } -#[cfg(test)] -#[cfg(feature = "embed")] +#[cfg(all(test, php_embed, feature = "link-php"))] mod tests { use crate::embed::Embed; diff --git a/src/zend/mod.rs b/src/zend/mod.rs index c7a74ac273..2915d4d651 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -34,7 +34,7 @@ pub use ini_entry_def::IniEntryDef; pub use linked_list::ZendLinkedList; pub use module::ModuleEntry; pub use streams::*; -#[cfg(feature = "embed")] +#[cfg(php_embed)] pub(crate) use try_catch::panic_wrapper; pub use try_catch::{bailout, try_catch, try_catch_first}; diff --git a/src/zend/try_catch.rs b/src/zend/try_catch.rs index 3af511b0e7..c8f432d146 100644 --- a/src/zend/try_catch.rs +++ b/src/zend/try_catch.rs @@ -102,8 +102,7 @@ pub unsafe fn bailout() -> ! { ext_php_rs_zend_bailout(); } -#[cfg(feature = "embed")] -#[cfg(test)] +#[cfg(all(test, php_embed, feature = "link-php"))] mod tests { use crate::embed::Embed; use crate::zend::{bailout, try_catch}; @@ -179,20 +178,22 @@ mod tests { #[test] fn test_memory_leak() { - let mut ptr = null_mut(); + Embed::run(|| { + let mut ptr = null_mut(); - let _ = try_catch(|| { - let mut result = "foo".to_string(); - ptr = &mut result; + let _ = try_catch(|| { + let mut result = "foo".to_string(); + ptr = &mut result; - unsafe { - bailout(); - } - }); + unsafe { + bailout(); + } + }); - // Check that the string is never released - let result = unsafe { &*ptr as &str }; + // Check that the string is never released + let result = unsafe { &*ptr as &str }; - assert_eq!(result, "foo"); + assert_eq!(result, "foo"); + }); } } diff --git a/tests/module.rs b/tests/module.rs index 7131ef6fe8..8c0b7ee18d 100644 --- a/tests/module.rs +++ b/tests/module.rs @@ -1,5 +1,6 @@ #![cfg_attr(windows, feature(abi_vectorcall))] -#![cfg(feature = "embed")] +#![cfg(all(php_embed, feature = "link-php"))] + extern crate ext_php_rs; use cfg_if::cfg_if; diff --git a/tests/sapi.rs b/tests/sapi.rs index def0bc7578..43cadb0376 100644 --- a/tests/sapi.rs +++ b/tests/sapi.rs @@ -1,5 +1,6 @@ #![cfg_attr(windows, feature(abi_vectorcall))] -#![cfg(feature = "embed")] +#![cfg(all(php_embed, feature = "link-php"))] + extern crate ext_php_rs; use ext_php_rs::builders::SapiBuilder; @@ -44,6 +45,9 @@ fn test_sapi() { } unsafe { + #[cfg(not(php82))] + php_module_startup(sapi, module, 1); + #[cfg(php82)] php_module_startup(sapi, module); } diff --git a/unix_build.rs b/unix_build.rs index 8be6fd8be9..4a5bd1efea 100644 --- a/unix_build.rs +++ b/unix_build.rs @@ -52,12 +52,21 @@ impl<'a> PHPProvider<'a> for Provider { .collect()) } + fn get_sapis(&self) -> Result> { + Ok(self + .php_config("--php-sapis")? + .split(' ') + .map(|s| s.trim_start_matches("-I")) + .map(|v| v.to_string()) + .collect()) + } + fn get_defines(&self) -> Result> { Ok(vec![]) } fn print_extra_link_args(&self) -> Result<()> { - #[cfg(feature = "embed")] + #[cfg(feature = "link-php")] println!("cargo:rustc-link-lib=php"); Ok(()) diff --git a/windows_build.rs b/windows_build.rs index ce2db78d21..5b69714419 100644 --- a/windows_build.rs +++ b/windows_build.rs @@ -49,6 +49,10 @@ impl<'a> PHPProvider<'a> for Provider<'a> { Ok(self.devel.include_paths()) } + fn get_sapis(&self) -> Result> { + Ok(vec!["embed".to_string()]) + } + fn get_defines(&self) -> Result> { let mut defines = vec![ ("ZEND_WIN32", "1"),