Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Compilation fix under Ubuntu #109

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open

Conversation

ogabrielides
Copy link
Collaborator

@ogabrielides ogabrielides commented Oct 17, 2024

Issue being fixed or feature implemented

Compilation under Ubuntu was failing.
This PR fixes issue #108.
Originally the code was checking minimum protobuf version as a float.
In Ubuntu although, the version was in the major, minor, patch format.

What was done?

  • Changed the minimum required version from float to String "x.x.x" format.
  • In Ubuntu protobuf version is around 3.10.x, while on other platforms it is 28 now: split the minimal version depending on the target os.

How Has This Been Tested?

Compiled locally in macOS and in an Ubuntu 22 VM.

Breaking Changes

no

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • New Features

    • Introduced support for semantic versioning with the addition of the semver library.
    • Added functionality to determine the required protoc version based on the operating system.
  • Changes

    • Updated version constants for protocol buffers to string representations for better precision.
    • Enhanced dependency checking logic to utilize semantic version comparisons.
  • Bug Fixes

    • Improved handling of protoc version checks to ensure compatibility across different environments.

Copy link
Contributor

coderabbitai bot commented Oct 17, 2024

Walkthrough

The changes introduce a new dependency on the semver crate in the Cargo.toml file of the tenderdash-proto-compiler package. In the constants.rs file, a floating-point constant for the protocol buffer version is replaced with two string constants for more precise versioning. The functions.rs file sees the addition of a new function to determine the required protoc version based on the operating system, along with modifications to existing functions to handle string-based version comparisons instead of floats.

Changes

File Change Summary
proto-compiler/Cargo.toml Added dependency: semver = "1.0"
proto-compiler/src/constants.rs Removed constant: pub const DEP_PROTOC_VERSION: f32 = 25.0;
Added constants: pub const DEP_PROTOC_VERSION_UBUNTU: &str = "3.12.4";
pub const DEP_PROTOC_VERSION_OTHER: &str = "25.0.0";
proto-compiler/src/functions.rs Added function: fn get_required_protoc_version() -> &'static str
Updated function signature: fn dep_protoc(expected_version: f32)fn dep_protoc(required_version_str: &str)

Poem

🐇 In the meadow where versions play,
New strings dance, come what may.
With semver now by our side,
Precision's our joyful guide.
Hop along, let's celebrate,
For better versions await! 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (1)
proto-compiler/src/constants.rs (1)

4-5: Improved version handling, but consider future maintainability

The change from a single f32 constant to two &str constants for different platforms is a good step towards addressing the compilation issues on Ubuntu. This allows for more precise version specification and accommodates the different versioning schemes used by Ubuntu and other platforms.

However, there are a few points to consider:

  1. Maintainability: Having separate constants for different platforms might lead to maintenance overhead in the future. Consider using a more dynamic approach to determine the required version based on the current platform.

  2. Flexibility: The current implementation assumes only two categories (Ubuntu and Others). This might not be flexible enough if other platforms require different versions in the future.

To improve maintainability and flexibility, consider the following suggestions:

  1. Use a configuration file or environment variables to specify version requirements for different platforms. This would allow easier updates without changing the code.

  2. Implement a function that determines the required version based on the current platform, possibly using the cfg attributes for conditional compilation. For example:

pub fn get_required_protoc_version() -> &'static str {
    #[cfg(target_os = "linux")]
    {
        if is_ubuntu() {
            "3.12.4"
        } else {
            "25.0.0"
        }
    }
    #[cfg(not(target_os = "linux"))]
    {
        "25.0.0"
    }
}

#[cfg(target_os = "linux")]
fn is_ubuntu() -> bool {
    // Implement logic to detect Ubuntu
    // This could involve checking /etc/os-release or using a crate like `os_info`
    todo!()
}

This approach would centralize version management and make it easier to add support for other platforms in the future.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a6da843 and 2dc53db.

📒 Files selected for processing (3)
  • proto-compiler/Cargo.toml (1 hunks)
  • proto-compiler/src/constants.rs (1 hunks)
  • proto-compiler/src/functions.rs (2 hunks)
🧰 Additional context used
🔇 Additional comments (1)
proto-compiler/Cargo.toml (1)

24-24: LGTM: Addition of semver dependency aligns with PR objectives.

The inclusion of the semver crate (version 1.0) is a good choice for improving version comparison capabilities. This directly supports the PR's goal of addressing the compilation issue on Ubuntu by enabling more robust handling of "x.x.x" version formats.

Comment on lines +364 to +376
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
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider refining OS detection to specifically identify Ubuntu

Currently, the get_required_protoc_version function uses #[cfg(target_os = "linux")] to determine if the operating system is Linux and then assumes it's Ubuntu by returning DEP_PROTOC_VERSION_UBUNTU. This may not be accurate for all Linux distributions, as different distributions may have different protoc versions. Consider implementing a more precise detection mechanism to specifically identify Ubuntu. This can help ensure that the correct required protoc version is used for the appropriate operating system.

Comment on lines +429 to +442
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()
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Unit tests depend on the system's installed protoc version

The test_protoc_dep function relies on the actual protoc version installed on the system where the tests are executed. This can lead to inconsistent test results across different environments, making the tests unreliable. To ensure consistent and reliable tests, consider mocking the calls to protoc or abstracting the system interactions so that you can inject test responses without depending on the environment's protoc installation.

For example, you can refactor dep_protoc to accept a function parameter that simulates the command output, allowing you to inject mock outputs during testing.

-fn dep_protoc(required_version_str: &str) -> Result<Version, String> {
+fn dep_protoc<F>(required_version_str: &str, get_protoc_version_output: F) -> Result<Version, String>
+    where F: Fn() -> Result<String, String>
{
-    let output = std::process::Command::new("protoc")
-        .arg("--version")
-        .output()
-        .map_err(|e| format!("Failed to execute protoc: {}", e))?;
-
-    let version_output = String::from_utf8(output.stdout)
-        .map_err(|e| format!("Invalid UTF-8 output from protoc: {}", e))?;
+    let version_output = get_protoc_version_output()?;

    // Parsing logic remains the same
}

Then, in your tests, you can provide a mock function:

#[test]
fn test_protoc_dep() {
    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 mock_output = Ok("libprotoc 28.0.0".to_string());
        let result = dep_protoc(required_version, |_| mock_output.clone());
        assert_eq!(
            result.is_ok(),
            expected_result,
            "Test case failed for required_version='{}', error='{:?}'",
            required_version,
            result.err()
        );
    }
}

Comment on lines 395 to 396
let installed_version_str = version_output.trim().split_whitespace().nth(1)
.ok_or_else(|| "Failed to parse protoc version output".to_string())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve robustness of parsing protoc --version output

The current implementation assumes that the output of protoc --version is always in the format "libprotoc X.Y.Z". However, there might be variations in this output across different platforms or versions. To enhance robustness, consider verifying that the output starts with "libprotoc" before attempting to parse the version number. This will help prevent unexpected errors if the output format differs.

Apply this diff to improve the parsing logic:

 let installed_version_str = version_output.trim().split_whitespace().nth(1)
     .ok_or_else(|| "Failed to parse protoc version output".to_string())?;
+ if !version_output.trim().starts_with("libprotoc") {
+     return Err("Unexpected protoc version output format".to_string());
+ }

Committable suggestion was skipped due to low confidence.

Comment on lines +416 to +422
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(".")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle pre-release and build metadata in version strings

The normalize_version function pads the version string to ensure it has three components. However, it does not account for pre-release identifiers or build metadata (e.g., "3.12.4-beta" or "3.12.4+meta"), which are valid in semantic versioning. This could lead to incorrect parsing or version comparisons. Consider updating the function to handle these cases or using Version::parse directly without normalization, as semver can parse incomplete versions and handle pre-release/build metadata appropriately.

Apply this diff to remove the normalization and rely on Version::parse:

-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(".")
-}

Update the version parsing in dep_protoc:

 let installed_version = Version::parse(installed_version_str)
     .map_err(|e| format!("Failed to parse installed protoc version '{}': {}", installed_version_str, e))?;

-let required_version = Version::parse(required_version_str)
+let required_version = Version::parse(&normalize_version(required_version_str))
     .map_err(|e| format!("Failed to parse required protoc version '{}': {}", required_version_str, e))?;

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (3)
proto-compiler/src/functions.rs (3)

367-379: LGTM: OS-specific version requirement implemented

The get_required_protoc_version function successfully implements the PR objective of splitting the minimum version requirement based on the target OS. This is a good approach to handle potential differences between Linux (assumed to be Ubuntu) and other systems.

However, consider the following improvement:

As noted in the comment, you might want to further refine the Linux detection to specifically identify Ubuntu. This could be done using the std::env::consts::OS and additional system information. For example:

#[cfg(target_os = "linux")]
{
    // Check if it's specifically Ubuntu
    if let Ok(os_release) = std::fs::read_to_string("/etc/os-release") {
        if os_release.contains("Ubuntu") {
            return DEP_PROTOC_VERSION_UBUNTU;
        }
    }
    // Default to OTHER for non-Ubuntu Linux
    DEP_PROTOC_VERSION_OTHER
}

This would provide more accurate version requirements for different Linux distributions.


386-428: LGTM: Improved version checking with semantic versioning

The modifications to dep_protoc successfully implement semantic versioning for more accurate and robust version comparisons. This addresses the PR objective of changing the version check from float to string format.

A minor suggestion for improvement:

Consider adding a check for the "libprotoc" prefix in the version output to ensure the parsing is correct. You can add this check right after parsing the version string:

let version_output = String::from_utf8(output.stdout)
    .map_err(|e| format!("Invalid UTF-8 output from protoc: {}", e))?;

if !version_output.trim().starts_with("libprotoc") {
    return Err("Unexpected protoc version output format".to_string());
}

// Rest of the function remains the same

This additional check will make the function more robust against unexpected protoc output formats.


431-457: LGTM: Version normalization and updated tests

The addition of the normalize_version function and the updates to the test case are good improvements. They ensure consistent version formatting and properly test the new string-based version comparison.

A suggestion for improvement:

The normalize_version function currently doesn't handle pre-release versions or build metadata (e.g., "3.14.0-beta" or "3.14.0+build.1"). Consider updating it to preserve this information:

fn normalize_version(version_str: &str) -> String {
    let mut parts: Vec<&str> = version_str.split(|c| c == '.' || c == '-' || c == '+').collect();
    let mut result = String::new();
    
    // Normalize the main version components
    for i in 0..3 {
        if i > 0 {
            result.push('.');
        }
        result.push_str(parts.get(i).unwrap_or(&"0"));
    }
    
    // Append pre-release version if present
    if let Some(pre_release) = version_str.split('-').nth(1) {
        result.push('-');
        result.push_str(pre_release);
    }
    
    // Append build metadata if present
    if let Some(build_meta) = version_str.split('+').nth(1) {
        result.push('+');
        result.push_str(build_meta);
    }
    
    result
}

This will ensure that the function handles a wider range of valid semantic version strings.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 2dc53db and a21d2f9.

📒 Files selected for processing (1)
  • proto-compiler/src/functions.rs (2 hunks)
🧰 Additional context used
🔇 Additional comments (3)
proto-compiler/src/functions.rs (3)

9-9: LGTM: Good use of semantic versioning

The addition of the semver crate is a good choice for handling version comparisons. This will provide more robust and standardized version handling throughout the code.


12-15: LGTM: Added new constant for Ubuntu-specific version

The addition of DEP_PROTOC_VERSION_UBUNTU allows for more flexible version requirements based on the operating system. This is a good approach to handle potential differences between Ubuntu and other systems.


Line range hint 1-457: Overall assessment: Good improvements with minor suggestions

The changes in this file successfully address the PR objectives by implementing OS-specific version requirements and improving the version comparison logic using semantic versioning. The code is now more robust and flexible.

Key improvements:

  1. OS-specific protoc version requirements
  2. Use of semantic versioning for accurate comparisons
  3. More robust error handling in version parsing and comparison
  4. Consistent version normalization

The suggestions provided in the review comments, if implemented, would further enhance the code's robustness and flexibility. Great work overall!

@lklimek
Copy link
Collaborator

lklimek commented Nov 27, 2024

I don't think we want to support Ubuntu-shipped versions of protoc, as these might be missing some features (like optional fields). I would suggest to refactor this PR to check installed version of protoc, and if it's not correct, display informative error message like 'Your version of protoc is not supported. Please install protoc version {} or above from https://github.com/protocolbuffers/protobuf/releases`.

@lklimek lklimek mentioned this pull request Nov 27, 2024
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants