Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/proc-macro-api/src/lib.rs2
-rw-r--r--crates/proc-macro-api/src/version.rs2
-rw-r--r--crates/proc-macro-srv/build.rs25
-rw-r--r--crates/proc-macro-srv/src/abis/mod.rs37
-rw-r--r--crates/proc-macro-srv/src/dylib.rs5
5 files changed, 62 insertions, 9 deletions
diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs
index dbf2fb37e7..d7010e825a 100644
--- a/crates/proc-macro-api/src/lib.rs
+++ b/crates/proc-macro-api/src/lib.rs
@@ -26,7 +26,7 @@ use crate::{
process::ProcMacroProcessSrv,
};
-pub use version::{read_dylib_info, RustCInfo};
+pub use version::{read_dylib_info, read_version, RustCInfo};
#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub enum ProcMacroKind {
diff --git a/crates/proc-macro-api/src/version.rs b/crates/proc-macro-api/src/version.rs
index 66fe16e94f..8cef971f33 100644
--- a/crates/proc-macro-api/src/version.rs
+++ b/crates/proc-macro-api/src/version.rs
@@ -102,7 +102,7 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'
/// * [some more bytes that we don't really care but about still there] :-)
/// Check this issue for more about the bytes layout:
/// <https://github.com/rust-lang/rust-analyzer/issues/6174>
-fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
+pub fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
let dylib_file = File::open(dylib_path)?;
let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?;
diff --git a/crates/proc-macro-srv/build.rs b/crates/proc-macro-srv/build.rs
new file mode 100644
index 0000000000..e3e4b9d87b
--- /dev/null
+++ b/crates/proc-macro-srv/build.rs
@@ -0,0 +1,25 @@
+use std::{env, fs::File, io::Write, path::PathBuf, process::Command};
+
+fn main() {
+ // Determine rustc version `proc-macro-srv` (and thus the sysroot ABI) is
+ // build with and make it accessible at runtime for ABI selection.
+
+ let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ path.push("rustc_version.rs");
+ let mut f = File::create(&path).unwrap();
+
+ let rustc = env::var("RUSTC").expect("proc-macro-srv's build script expects RUSTC to be set");
+ let output = Command::new(rustc).arg("--version").output().expect("rustc --version must run");
+ let version_string = std::str::from_utf8(&output.stdout[..])
+ .expect("rustc --version output must be UTF-8")
+ .trim();
+
+ write!(
+ f,
+ "
+ #[allow(dead_code)]
+ pub(crate) const RUSTC_VERSION_STRING: &str = {version_string:?};
+ "
+ )
+ .unwrap();
+}
diff --git a/crates/proc-macro-srv/src/abis/mod.rs b/crates/proc-macro-srv/src/abis/mod.rs
index f1083a9284..a59da0f6b1 100644
--- a/crates/proc-macro-srv/src/abis/mod.rs
+++ b/crates/proc-macro-srv/src/abis/mod.rs
@@ -29,6 +29,9 @@ mod abi_1_64;
#[cfg(feature = "sysroot-abi")]
mod abi_sysroot;
+// see `build.rs`
+include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
+
// Used by `test/utils.rs`
#[cfg(test)]
pub(crate) use abi_1_64::TokenStream as TestTokenStream;
@@ -74,13 +77,37 @@ impl Abi {
lib: &Library,
symbol_name: String,
info: RustCInfo,
+ #[cfg_attr(not(feature = "sysroot-abi"), allow(unused_variables))] version_string: String,
) -> Result<Abi, LoadProcMacroDylibError> {
- // Gated behind an env var for now to avoid a change in behavior for
- // rustup-installed rust-analyzer
+ // the sysroot ABI relies on `extern proc_macro` with unstable features,
+ // instead of a snapshot of the proc macro bridge's source code. it's only
+ // enabled if we have an exact version match.
#[cfg(feature = "sysroot-abi")]
- if std::env::var("PROC_MACRO_SRV_SYSROOT_ABI").is_ok() {
- let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?;
- return Ok(Abi::AbiSysroot(inner));
+ {
+ if version_string == RUSTC_VERSION_STRING {
+ let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?;
+ return Ok(Abi::AbiSysroot(inner));
+ }
+
+ // if we reached this point, versions didn't match. in testing, we
+ // want that to panic - this could mean that the format of `rustc
+ // --version` no longer matches the format of the version string
+ // stored in the `.rustc` section, and we want to catch that in-tree
+ // with `x.py test`
+ #[cfg(test)]
+ {
+ let allow_mismatch = std::env::var("PROC_MACRO_SRV_ALLOW_SYSROOT_MISMATCH");
+ if let Ok("1") = allow_mismatch.as_deref() {
+ // only used by rust-analyzer developers, when working on the
+ // sysroot ABI from the rust-analyzer repository - which should
+ // only happen pre-subtree. this can be removed later.
+ } else {
+ panic!(
+ "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}",
+ version_string, RUSTC_VERSION_STRING
+ );
+ }
+ }
}
// FIXME: this should use exclusive ranges when they're stable
diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs
index 2b6c070fec..6439fb2130 100644
--- a/crates/proc-macro-srv/src/dylib.rs
+++ b/crates/proc-macro-srv/src/dylib.rs
@@ -12,7 +12,7 @@ use libloading::Library;
use memmap2::Mmap;
use object::Object;
use paths::AbsPath;
-use proc_macro_api::{read_dylib_info, ProcMacroKind};
+use proc_macro_api::{read_dylib_info, read_version, ProcMacroKind};
use super::abis::Abi;
@@ -122,9 +122,10 @@ impl ProcMacroLibraryLibloading {
invalid_data_err(format!("expected an absolute path, got {}", file.display()))
})?;
let version_info = read_dylib_info(abs_file)?;
+ let version_string = read_version(abs_file)?;
let lib = load_library(file).map_err(invalid_data_err)?;
- let abi = Abi::from_lib(&lib, symbol_name, version_info)?;
+ let abi = Abi::from_lib(&lib, symbol_name, version_info, version_string)?;
Ok(ProcMacroLibraryLibloading { _lib: lib, abi })
}
}