diff --git a/proto-compiler/Cargo.toml b/proto-compiler/Cargo.toml index 014adc9..499aa5a 100644 --- a/proto-compiler/Cargo.toml +++ b/proto-compiler/Cargo.toml @@ -21,6 +21,7 @@ ureq = { "version" = "2.10" } zip = { version = "2.2", default-features = false, features = ["deflate"] } fs_extra = { version = "1.3.0" } tonic-build = { version = "0.12", optional = true } +semver = "1.0" [features] diff --git a/proto-compiler/src/constants.rs b/proto-compiler/src/constants.rs index f5f7fec..8a94de5 100644 --- a/proto-compiler/src/constants.rs +++ b/proto-compiler/src/constants.rs @@ -1,7 +1,8 @@ //! Tenderdash protobuf implementation // Requirements -pub const DEP_PROTOC_VERSION: f32 = 25.0; +pub const DEP_PROTOC_VERSION_UBUNTU: &str = "3.12.4"; +pub const DEP_PROTOC_VERSION_OTHER: &str = "25.0.0"; /// Tenderdash repository URL. pub const TENDERDASH_REPO: &str = "https://github.com/dashpay/tenderdash"; diff --git a/proto-compiler/src/functions.rs b/proto-compiler/src/functions.rs index 7016fbb..2497f70 100644 --- a/proto-compiler/src/functions.rs +++ b/proto-compiler/src/functions.rs @@ -6,9 +6,13 @@ use std::{ process::Command, }; +use semver::Version; use walkdir::WalkDir; -use crate::constants::{GenerationMode, DEFAULT_TENDERDASH_COMMITISH, DEP_PROTOC_VERSION}; +use crate::constants::{ + GenerationMode, DEFAULT_TENDERDASH_COMMITISH, DEP_PROTOC_VERSION_OTHER, + DEP_PROTOC_VERSION_UBUNTU, +}; /// Check out a specific commitish of the tenderdash repository. /// @@ -360,53 +364,97 @@ pub(crate) fn check_state(dir: &Path, commitish: &str) -> bool { } } +fn get_required_protoc_version() -> &'static str { + #[cfg(target_os = "linux")] + { + // Further refine detection if needed + // For example, detect if it's Ubuntu + DEP_PROTOC_VERSION_UBUNTU + } + + #[cfg(not(target_os = "linux"))] + { + DEP_PROTOC_VERSION_OTHER + } +} + /// Check if all dependencies are met pub(crate) fn check_deps() -> Result<(), String> { - dep_protoc(DEP_PROTOC_VERSION).map(|_| ()) + dep_protoc(get_required_protoc_version()).map(|_| ()) } -/// Check if protoc is installed and has the required version -fn dep_protoc(expected_version: f32) -> Result { - let protoc = prost_build::protoc_from_env(); - - // Run `protoc --version` and capture the output - let output = Command::new(protoc) +fn dep_protoc(required_version_str: &str) -> Result { + // Get the installed protoc version + let output = std::process::Command::new("protoc") .arg("--version") .output() - .map_err(|e| format!("failed to run: {}", e))?; + .map_err(|e| format!("Failed to execute protoc: {}", e))?; - // Convert the output to a string - let out = output.stdout; - let version_output = String::from_utf8(out.clone()) - .map_err(|e| format!("output {:?} is not utf8 string: {}", out, e))?; + let version_output = String::from_utf8(output.stdout) + .map_err(|e| format!("Invalid UTF-8 output from protoc: {}", e))?; - // Extract the version number from string like `libprotoc 25.1` - let version: f32 = version_output + // Extract the version number from the output + // Assuming the output is like "libprotoc 3.12.4" + let installed_version_str = version_output + .trim() .split_whitespace() - .last() - .unwrap() - .parse() - .map_err(|e| format!("failed to parse protoc version {}: {}", version_output, e))?; + .nth(1) + .ok_or_else(|| "Failed to parse protoc version output".to_string())?; + + // Parse the versions + let installed_version = + Version::parse(&normalize_version(installed_version_str)).map_err(|e| { + format!( + "Failed to parse installed protoc version '{}': {}", + installed_version_str, e + ) + })?; - if version < expected_version { + let required_version = Version::parse(required_version_str).map_err(|e| { + format!( + "Failed to parse required protoc version '{}': {}", + required_version_str, e + ) + })?; + + // Compare versions + if installed_version >= required_version { + Ok(installed_version) + } else { Err(format!( - "protoc version must be {} or higher, but found {}; please upgrade: https://github.com/protocolbuffers/protobuf/releases/", - expected_version, version + "Installed protoc version {} is less than required version {}", + installed_version, required_version )) - } else { - Ok(version) } } +fn normalize_version(version_str: &str) -> String { + let mut parts: Vec<&str> = version_str.split('.').collect(); + while parts.len() < 3 { + parts.push("0"); + } + parts.join(".") +} #[cfg(test)] mod tests { use super::*; #[test] fn test_protoc_dep() { - let expected_versions = vec![(10.1, true), (DEP_PROTOC_VERSION, true), (90.5, false)]; - for expect in expected_versions { - assert_eq!(dep_protoc(expect.0).is_ok(), expect.1); + let expected_versions = vec![ + ("10.1.0", true), + (DEP_PROTOC_VERSION_OTHER, true), + ("90.5.0", false), + ]; + for &(required_version, expected_result) in &expected_versions { + let result = dep_protoc(required_version); + assert_eq!( + result.is_ok(), + expected_result, + "Test case failed for required_version='{}', error='{:?}'", + required_version, + result.err() + ); } } }