diff --git a/crates/ocs_plugin_api/ARCHITECTURE.md b/crates/ocs_plugin_api/ARCHITECTURE.md index 81b7b429e..9db51f42a 100644 --- a/crates/ocs_plugin_api/ARCHITECTURE.md +++ b/crates/ocs_plugin_api/ARCHITECTURE.md @@ -54,7 +54,7 @@ The runtime enforces three gates: 1. `ocs_plugin_api_version()` exported by the cdylib must be within `[API_VERSION_MIN_SUPPORTED, effective_max_api_version()]`. 2. For v4+, `acadrust_sources_compatible(host_acadrust_source(), plugin_acadrust_source)` must be true. -3. For v4+, when the plugin declares `rustc_version`, `rustc_versions_compatible(host_rustc_version(), plugin_rustc_version)` must be true. +3. For v4+, the plugin must declare `rustc_version`, and `rustc_versions_compatible(host_rustc_version(), plugin_rustc_version)` must be true. --- diff --git a/crates/ocs_plugin_api/build.rs b/crates/ocs_plugin_api/build.rs index 5be2df03e..3a1a38e50 100644 --- a/crates/ocs_plugin_api/build.rs +++ b/crates/ocs_plugin_api/build.rs @@ -384,7 +384,8 @@ fn generate_version_info(out_dir: &Path) { .find(|p| p.name.as_str() == "acadrust") .expect("acadrust package in Cargo.lock"); - let rustc_version = Command::new("rustc") + let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); + let rustc_version = Command::new(rustc) .arg("--version") .output() .ok() diff --git a/docs/plugin-architecture.md b/docs/plugin-architecture.md index d4a97e5a6..6cf934d7d 100644 --- a/docs/plugin-architecture.md +++ b/docs/plugin-architecture.md @@ -99,10 +99,10 @@ affect whether a prebuilt plugin can safely be loaded: - `rustc_version` — the output of `rustc --version` for the compiler that built the host. -A plugin's `plugin.toml` may declare the same values under `[opencad]`. When -either is declared, the host refuses to load the plugin unless it matches the -host exactly. This turns the current silent runner crash into a one-line -diagnosis such as: +A plugin's `plugin.toml` declares the same values under `[opencad]`. For API v4 +and later, `rustc_version` is required and the host refuses to load the plugin +unless it matches exactly. This turns the current silent runner crash into a +one-line diagnosis such as: ``` Plugin built with rustc 1.96.0, host requires 1.98.0 - rebuild required @@ -229,12 +229,12 @@ version = "0.1.0" description = "…" [opencad] -api_version = 3 +api_version = 4 ribbon_order = 50 command_prefixes = ["EX_"] xdata_apps = [] -# For API v4+, also record the exact rustc used to build the release asset. -# rustc_version = "rustc 1.98.0 (hash date)" +# Filled with the exact compiler output while staging the release asset. +rustc_version = "rustc 1.98.0 (hash date)" ``` The full, buildable scaffold is in [`docs/plugin-template/`](plugin-template); diff --git a/docs/plugin-template/.github/workflows/release.yml b/docs/plugin-template/.github/workflows/release.yml index 2ff8de5f3..76588a1eb 100644 --- a/docs/plugin-template/.github/workflows/release.yml +++ b/docs/plugin-template/.github/workflows/release.yml @@ -36,7 +36,7 @@ jobs: run: | mkdir -p dist cp target/release/*.${{ matrix.ext }} "dist/${{ matrix.asset }}" - cp plugin.toml dist/plugin.toml + sed "s|__RUSTC_VERSION__|$(rustc --version)|" plugin.toml > dist/plugin.toml - uses: softprops/action-gh-release@v2 with: files: | diff --git a/docs/plugin-template/README.md b/docs/plugin-template/README.md index f6113df1a..52583adaf 100644 --- a/docs/plugin-template/README.md +++ b/docs/plugin-template/README.md @@ -28,7 +28,8 @@ folder into a new repo and rename the placeholders. cargo build --release mkdir -p "/OpenCADStudio/plugins/opencad.my_plugin" cp target/release/*my_plugin*.so "/OpenCADStudio/plugins/opencad.my_plugin/" -cp plugin.toml "/OpenCADStudio/plugins/opencad.my_plugin/" +sed "s|__RUSTC_VERSION__|$(rustc --version)|" plugin.toml > \ + "/OpenCADStudio/plugins/opencad.my_plugin/plugin.toml" ``` Restart Open CAD Studio: the ribbon tab appears and `MP_` commands route to your diff --git a/docs/plugin-template/plugin.toml b/docs/plugin-template/plugin.toml index f2718db54..c594522d9 100644 --- a/docs/plugin-template/plugin.toml +++ b/docs/plugin-template/plugin.toml @@ -8,7 +8,8 @@ license = "GPL-3.0-only" repository = "your-account/your-plugin-repo" [opencad] -api_version = 3 +api_version = 4 +rustc_version = "__RUSTC_VERSION__" ribbon_order = 60 command_prefixes = ["MP_"] xdata_apps = ["MYPLUGIN_RECORD"] diff --git a/src/plugin/external.rs b/src/plugin/external.rs index 40f63bd1b..493696def 100644 --- a/src/plugin/external.rs +++ b/src/plugin/external.rs @@ -100,9 +100,6 @@ impl ExternalPlugin { if !ocs_plugin_api::version_info::uses_acadrust_gate(self.api_version) { return true; } - if !self.rustc_declared { - return true; - } match self.rustc_version.as_deref() { None | Some("") => false, Some(version) => ocs_plugin_api::version_info::rustc_versions_compatible( @@ -534,7 +531,7 @@ xdata_apps = ["MYPLUGIN_RECORD"] } #[test] - fn undeclared_rustc_falls_back_to_api_gate() { + fn undeclared_rustc_is_incompatible() { let toml = r#" [plugin] id = "opencad.test" @@ -545,7 +542,8 @@ api_version = 4 let p = parse_plugin_toml(toml).expect("parsed"); assert!(!p.rustc_declared); assert!(p.rustc_version.is_none()); - assert!(p.rustc_compatible(), "undeclared rustc is treated as compatible"); + assert!(!p.rustc_compatible()); + assert!(!p.loadable()); } #[test] diff --git a/src/plugin/marketplace.rs b/src/plugin/marketplace.rs index a89f4b4e5..1cd316e8e 100644 --- a/src/plugin/marketplace.rs +++ b/src/plugin/marketplace.rs @@ -242,8 +242,6 @@ pub fn fetch_release_info(repo: &str) -> Result, String> { manifest.api_version, ) { true - } else if !rustc_declared { - true } else { match rustc_version.as_deref() { None | Some("") => false, @@ -281,17 +279,6 @@ pub fn fetch_release_info(repo: &str) -> Result, String> { } } } - let any_rustc_declared = info.iter().any(|r| r.rustc_declared); - if any_rustc_declared { - for r in &mut info { - if !r.rustc_declared - && ocs_plugin_api::version_info::uses_acadrust_gate(r.api_version) - { - r.rustc_compatible = false; - } - } - } - if info.is_empty() { Err(last_error.unwrap_or_else(|| "no installable releases found".to_string())) } else { @@ -391,18 +378,12 @@ pub fn install(release: &Release, repository: &str) -> Result { } } - if ocs_plugin_api::version_info::uses_acadrust_gate(manifest.api_version) - && manifest.rustc_declared - { + if ocs_plugin_api::version_info::uses_acadrust_gate(manifest.api_version) { let Some(version) = manifest.rustc_version.as_deref() else { - return Err( - "Release declares rustc metadata but has no version; cannot verify ABI compatibility".to_string(), - ); + return Err("Release has no rustc version; cannot verify ABI compatibility".to_string()); }; if version.is_empty() { - return Err( - "Release declares rustc metadata but has no version; cannot verify ABI compatibility".to_string(), - ); + return Err("Release has no rustc version; cannot verify ABI compatibility".to_string()); } if !ocs_plugin_api::version_info::rustc_versions_compatible( version, diff --git a/src/ui/window/plugin_manager.rs b/src/ui/window/plugin_manager.rs index 2db1bdd31..341fc09a6 100644 --- a/src/ui/window/plugin_manager.rs +++ b/src/ui/window/plugin_manager.rs @@ -226,7 +226,8 @@ fn external_card<'a>( ) -> Element<'a, Message> { let failed_old_api = load_error.is_some() && !p.api_compatible(); let acadrust_mismatch = p.acadrust_declared && !p.acadrust_compatible(); - let rustc_mismatch = p.rustc_declared && !p.rustc_compatible(); + let rustc_mismatch = ocs_plugin_api::version_info::uses_acadrust_gate(p.api_version) + && !p.rustc_compatible(); let (status, kind) = if loaded && disabled { (t!("Disabled"), StatusKind::Muted) } else if loaded {