forked from 1b-agent/opencadstudio-upstream
fix(plugins): enforce rustc compatibility
BREAKING CHANGE: API v4+ plugin packages must declare the exact rustc version used to build the binary. Refs #901
This commit is contained in:
1 parent
4668042df5
commit
466ab99c63
9 files changed
+23
-40
No files matched your search
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -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: |
|
||||
|
||||
@@ -28,7 +28,8 @@ folder into a new repo and rename the placeholders.
|
||||
cargo build --release
|
||||
mkdir -p "<config>/OpenCADStudio/plugins/opencad.my_plugin"
|
||||
cp target/release/*my_plugin*.so "<config>/OpenCADStudio/plugins/opencad.my_plugin/"
|
||||
cp plugin.toml "<config>/OpenCADStudio/plugins/opencad.my_plugin/"
|
||||
sed "s|__RUSTC_VERSION__|$(rustc --version)|" plugin.toml > \
|
||||
"<config>/OpenCADStudio/plugins/opencad.my_plugin/plugin.toml"
|
||||
```
|
||||
|
||||
Restart Open CAD Studio: the ribbon tab appears and `MP_` commands route to your
|
||||
|
||||
@@ -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"]
|
||||
@@ -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]
|
||||
|
||||
@@ -242,8 +242,6 @@ pub fn fetch_release_info(repo: &str) -> Result<Vec<ReleaseInfo>, 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<Vec<ReleaseInfo>, 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<String, String> {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user