about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs27
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs48
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs24
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/version.rs58
4 files changed, 139 insertions, 18 deletions
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index 278d9cbcda4..fe274a027a8 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -63,16 +63,25 @@ impl ProcMacroServerProcess {
         let mut srv = create_srv()?;
         tracing::info!("sending proc-macro server version check");
         match srv.version_check() {
-            Ok(v) if v > version::CURRENT_API_VERSION => Err(io::Error::other(
-                format!( "The version of the proc-macro server ({v}) in your Rust toolchain is newer than the version supported by your rust-analyzer ({}).
-                        This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain."
-                        ,version::CURRENT_API_VERSION
-                    ),
-                )),
+            Ok(v) if v > version::CURRENT_API_VERSION => {
+                #[allow(clippy::disallowed_methods)]
+                let process_version = Command::new(process_path)
+                    .arg("--version")
+                    .output()
+                    .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned())
+                    .unwrap_or_else(|_| "unknown version".to_owned());
+                Err(io::Error::other(format!(
+                    "Your installed proc-macro server is too new for your rust-analyzer. API version: {}, server version: {process_version}. \
+                        This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain.",
+                    version::CURRENT_API_VERSION
+                )))
+            }
             Ok(v) => {
                 tracing::info!("Proc-macro server version: {v}");
                 srv.version = v;
-                if srv.version >= version::RUST_ANALYZER_SPAN_SUPPORT && let Ok(mode) = srv.enable_rust_analyzer_spans() {
+                if srv.version >= version::RUST_ANALYZER_SPAN_SUPPORT
+                    && let Ok(mode) = srv.enable_rust_analyzer_spans()
+                {
                     srv.protocol = Protocol::LegacyJson { mode };
                 }
                 tracing::info!("Proc-macro server protocol: {:?}", srv.protocol);
@@ -80,9 +89,7 @@ impl ProcMacroServerProcess {
             }
             Err(e) => {
                 tracing::info!(%e, "proc-macro version check failed");
-                Err(
-                    io::Error::other(format!("proc-macro server version check failed: {e}")),
-                )
+                Err(io::Error::other(format!("proc-macro server version check failed: {e}")))
             }
         }
     }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs
index 07f914fece0..12e7c8b05ba 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs
@@ -1,5 +1,49 @@
-//! This teaches cargo about our cfg(rust_analyzer)
+//! Construct version in the `commit-hash date channel` format
+
+use std::{env, path::PathBuf, process::Command};
 
 fn main() {
-    println!("cargo:rustc-check-cfg=cfg(rust_analyzer)");
+    set_rerun();
+    set_commit_info();
+    println!("cargo::rustc-check-cfg=cfg(rust_analyzer)");
+}
+
+fn set_rerun() {
+    println!("cargo:rerun-if-env-changed=CFG_RELEASE");
+
+    let mut manifest_dir = PathBuf::from(
+        env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."),
+    );
+
+    while manifest_dir.parent().is_some() {
+        let head_ref = manifest_dir.join(".git/HEAD");
+        if head_ref.exists() {
+            println!("cargo:rerun-if-changed={}", head_ref.display());
+            return;
+        }
+
+        manifest_dir.pop();
+    }
+
+    println!("cargo:warning=Could not find `.git/HEAD` from manifest dir!");
+}
+
+fn set_commit_info() {
+    #[allow(clippy::disallowed_methods)]
+    let output = match Command::new("git")
+        .arg("log")
+        .arg("-1")
+        .arg("--date=short")
+        .arg("--format=%H %h %cd")
+        .output()
+    {
+        Ok(output) if output.status.success() => output,
+        _ => return,
+    };
+    let stdout = String::from_utf8(output.stdout).unwrap();
+    let mut parts = stdout.split_whitespace();
+    let mut next = || parts.next().unwrap();
+    println!("cargo:rustc-env=RA_COMMIT_HASH={}", next());
+    println!("cargo:rustc-env=RA_COMMIT_SHORT_HASH={}", next());
+    println!("cargo:rustc-env=RA_COMMIT_DATE={}", next())
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
index 97a622e453d..662d34865ef 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
@@ -2,11 +2,13 @@
 //! Driver for proc macro server
 #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
 #![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))]
-#![allow(clippy::print_stderr)]
+#![allow(clippy::print_stdout, clippy::print_stderr)]
 
 #[cfg(feature = "in-rust-tree")]
 extern crate rustc_driver as _;
 
+mod version;
+
 #[cfg(any(feature = "sysroot-abi", rust_analyzer))]
 mod main_loop;
 use clap::{Command, ValueEnum};
@@ -25,12 +27,22 @@ fn main() -> std::io::Result<()> {
         std::process::exit(122);
     }
     let matches = Command::new("proc-macro-srv")
-        .args(&[clap::Arg::new("format")
-            .long("format")
-            .action(clap::ArgAction::Set)
-            .default_value("json")
-            .value_parser(clap::builder::EnumValueParser::<ProtocolFormat>::new())])
+        .args(&[
+            clap::Arg::new("format")
+                .long("format")
+                .action(clap::ArgAction::Set)
+                .default_value("json")
+                .value_parser(clap::builder::EnumValueParser::<ProtocolFormat>::new()),
+            clap::Arg::new("version")
+                .long("version")
+                .action(clap::ArgAction::SetTrue)
+                .help("Prints the version of the proc-macro-srv"),
+        ])
         .get_matches();
+    if matches.get_flag("version") {
+        println!("rust-analyzer-proc-macro-srv {}", version::version());
+        return Ok(());
+    }
     let &format =
         matches.get_one::<ProtocolFormat>("format").expect("format value should always be present");
     run(format)
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/version.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/version.rs
new file mode 100644
index 00000000000..32499d055d1
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/version.rs
@@ -0,0 +1,58 @@
+//! Code for representing rust-analyzer's release version number.
+#![expect(dead_code)]
+
+use std::fmt;
+
+/// Information about the git repository where rust-analyzer was built from.
+pub(crate) struct CommitInfo {
+    pub(crate) short_commit_hash: &'static str,
+    pub(crate) commit_hash: &'static str,
+    pub(crate) commit_date: &'static str,
+}
+
+/// Cargo's version.
+pub(crate) struct VersionInfo {
+    /// rust-analyzer's version, such as "1.57.0", "1.58.0-beta.1", "1.59.0-nightly", etc.
+    pub(crate) version: &'static str,
+    /// The release channel we were built for (stable/beta/nightly/dev).
+    ///
+    /// `None` if not built via bootstrap.
+    pub(crate) release_channel: Option<&'static str>,
+    /// Information about the Git repository we may have been built from.
+    ///
+    /// `None` if not built from a git repo.
+    pub(crate) commit_info: Option<CommitInfo>,
+}
+
+impl fmt::Display for VersionInfo {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.version)?;
+
+        if let Some(ci) = &self.commit_info {
+            write!(f, " ({} {})", ci.short_commit_hash, ci.commit_date)?;
+        };
+        Ok(())
+    }
+}
+
+/// Returns information about cargo's version.
+pub(crate) const fn version() -> VersionInfo {
+    let version = match option_env!("CFG_RELEASE") {
+        Some(x) => x,
+        None => "0.0.0",
+    };
+
+    let release_channel = option_env!("CFG_RELEASE_CHANNEL");
+    let commit_info = match (
+        option_env!("RA_COMMIT_SHORT_HASH"),
+        option_env!("RA_COMMIT_HASH"),
+        option_env!("RA_COMMIT_DATE"),
+    ) {
+        (Some(short_commit_hash), Some(commit_hash), Some(commit_date)) => {
+            Some(CommitInfo { short_commit_hash, commit_hash, commit_date })
+        }
+        _ => None,
+    };
+
+    VersionInfo { version, release_channel, commit_info }
+}