Skip to content

Commit

Permalink
spawn version compatibility check (#4031)
Browse files Browse the repository at this point in the history
  • Loading branch information
teh-cmc authored Oct 27, 2023
1 parent 2b05c23 commit c929c34
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 25 deletions.
51 changes: 51 additions & 0 deletions crates/re_build_info/src/build_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,54 @@ impl std::fmt::Display for BuildInfo {
Ok(())
}
}

// ---

use crate::CrateVersion;

impl CrateVersion {
/// Attempts to parse a [`CrateVersion`] from a [`BuildInfo`]'s string representation (`rerun --version`).
///
/// Refer to `BuildInfo as std::fmt::Display>::fmt` to see what the string representation is
/// expected to look like. Roughly:
/// ```ignore
/// <name> <semver> [<rust_info>] <target> <branch> <commit> <build_date>
/// ```
pub fn try_parse_from_build_info_string(s: impl AsRef<str>) -> Result<CrateVersion, String> {
let s = s.as_ref();
let parts = s.split_whitespace().collect::<Vec<_>>();
if parts.len() < 2 {
return Err(format!("{s:?} is not a valid BuildInfo string"));
}
CrateVersion::try_parse(parts[1]).map_err(ToOwned::to_owned)
}
}

#[test]
fn crate_version_from_build_info_string() {
let build_info = BuildInfo {
crate_name: "re_build_info",
version: CrateVersion {
major: 0,
minor: 10,
patch: 0,
meta: Some(crate::crate_version::Meta::DevAlpha(7)),
},
rustc_version: "1.72.1 (d5c2e9c34 2023-09-13)",
llvm_version: "16.0.5",
git_hash: "",
git_branch: "",
is_in_rerun_workspace: true,
target_triple: "x86_64-unknown-linux-gnu",
datetime: "",
};

let build_info_str = build_info.to_string();

{
let expected_crate_version = build_info.version;
let crate_version = CrateVersion::try_parse_from_build_info_string(build_info_str).unwrap();

assert_eq!(expected_crate_version, crate_version);
}
}
8 changes: 4 additions & 4 deletions crates/re_build_info/src/crate_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ mod meta {
/// - `00000000` -> none of the above
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CrateVersion {
major: u8,
minor: u8,
patch: u8,
meta: Option<Meta>,
pub major: u8,
pub minor: u8,
pub patch: u8,
pub meta: Option<Meta>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down
98 changes: 77 additions & 21 deletions crates/re_sdk/src/spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@ impl std::fmt::Debug for SpawnError {
pub fn spawn(opts: &SpawnOptions) -> Result<(), SpawnError> {
use std::{net::TcpStream, process::Command, time::Duration};

// NOTE: It's indented on purpose, it just looks better and reads easier.
const EXECUTABLE_NOT_FOUND: &str = //
// NOTE: These are indented on purpose, it just looks better and reads easier.

const MSG_INSTALL_HOW_TO: &str = //
"
You can install binary releases of the Rerun Viewer:
* Using `cargo`: `cargo binstall rerun-cli` (see https://github.com/cargo-bins/cargo-binstall)
Expand All @@ -142,6 +143,26 @@ pub fn spawn(opts: &SpawnOptions) -> Result<(), SpawnError> {
https://rerun.io/docs/getting-started/installing-viewer
";

const MSG_INSTALL_HOW_TO_VERSIONED: &str = //
"
You can install an appropriate version of the Rerun Viewer via binary releases:
* Using `cargo`: `cargo binstall --force rerun-cli@__VIEWER_VERSION__` (see https://github.com/cargo-bins/cargo-binstall)
* Via direct download from our release assets: https://github.com/rerun-io/rerun/releases/__VIEWER_VERSION__/
* Using `pip`: `pip3 install rerun-sdk==__VIEWER_VERSION__` (warning: pip version has slower start times!)
For more information, refer to our complete install documentation over at:
https://rerun.io/docs/getting-started/installing-viewer
";

const MSG_VERSION_MISMATCH: &str = //
"
⚠ The version of the Rerun Viewer available on your PATH does not match the version of your Rerun SDK ⚠
Rerun does not make any kind of backwards/forwards compatibility guarantee yet: this can lead to (subtle) bugs.
> Rerun Viewer: v__VIEWER_VERSION__ (executable: \"__VIEWER_PATH__\")
> Rerun SDK: v__SDK_VERSION__";

let port = opts.port;
let connect_addr = opts.connect_addr();
let memory_limit = &opts.memory_limit;
Expand All @@ -156,33 +177,68 @@ pub fn spawn(opts: &SpawnOptions) -> Result<(), SpawnError> {
return Ok(());
}

let res = Command::new(executable_path)
.arg(format!("--port={port}"))
.arg(format!("--memory-limit={memory_limit}"))
.arg("--skip-welcome-screen")
.args(opts.extra_args.clone())
.spawn();

let rerun_bin = match res {
Ok(rerun_bin) => rerun_bin,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
return if let Some(executable_path) = opts.executable_path.as_ref() {
Err(SpawnError::ExecutableNotFound {
let map_err = |err: std::io::Error| -> SpawnError {
if err.kind() == std::io::ErrorKind::NotFound {
if let Some(executable_path) = opts.executable_path.as_ref() {
SpawnError::ExecutableNotFound {
executable_path: executable_path.clone(),
})
}
} else {
Err(SpawnError::ExecutableNotFoundInPath {
message: EXECUTABLE_NOT_FOUND.to_owned(),
SpawnError::ExecutableNotFoundInPath {
message: MSG_INSTALL_HOW_TO.to_owned(),
executable_name: opts.executable_name.clone(),
search_path: std::env::var("PATH").unwrap_or_else(|_| String::new()),
})
}
}
} else {
err.into()
}
Err(err) => {
return Err(err.into());
}
};

let viewer_version = {
let output = Command::new(&executable_path)
.arg("--version")
.output()
.map_err(map_err)?;

let output = String::from_utf8_lossy(&output.stdout);
re_build_info::CrateVersion::try_parse_from_build_info_string(output).ok()
};

if let Some(viewer_version) = viewer_version {
let sdk_version = re_build_info::build_info!().version;

if !viewer_version.is_compatible_with(sdk_version) {
eprintln!(
"{}",
MSG_VERSION_MISMATCH
.replace("__VIEWER_VERSION__", &viewer_version.to_string())
.replace("__VIEWER_PATH__", &executable_path)
.replace("__SDK_VERSION__", &sdk_version.to_string())
);

// Don't recommend installing stuff through registries if the user is running some
// weird version.
if sdk_version.meta.is_none() {
eprintln!(
"{}",
MSG_INSTALL_HOW_TO_VERSIONED
.replace("__VIEWER_VERSION__", &sdk_version.to_string())
);
} else {
eprintln!();
}
}
}

let rerun_bin = Command::new(&executable_path)
.arg(format!("--port={port}"))
.arg(format!("--memory-limit={memory_limit}"))
.arg("--skip-welcome-screen")
.args(opts.extra_args.clone())
.spawn()
.map_err(map_err);

// Simply forget about the child process, we want it to outlive the parent process if needed.
_ = rerun_bin;

Expand Down

0 comments on commit c929c34

Please sign in to comment.