Skip to content

Commit

Permalink
feat(wasm_builder): Multiple crate versions check (#4159)
Browse files Browse the repository at this point in the history
Co-authored-by: Dmitrii Novikov <[email protected]>
  • Loading branch information
vobradovich and breathx authored Aug 27, 2024
1 parent b7c2683 commit 81fe12d
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ errno = "0.3" # gear-lazy-pages
nix = "0.26.4" # gear-lazy-pages
indexmap = "2.2.6" # utils/weight-diff
indicatif = "*" # utils/wasm-gen
itertools = "0.13" # utils/wasm-builder
keyring = "1.2.1" # gcli
libp2p = "=0.51.4" # gcli (same version as sc-consensus)
mimalloc = { version = "0.1.43", default-features = false } # node/cli
Expand Down
1 change: 1 addition & 0 deletions utils/wasm-builder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ gear-core.workspace = true
gear-wasm-instrument.workspace = true
gear-wasm-optimizer.workspace = true
rustc_version.workspace = true
itertools.workspace = true

[dev-dependencies]
wabt.workspace = true
Expand Down
5 changes: 4 additions & 1 deletion utils/wasm-builder/src/crate_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use anyhow::{Context, Result};
use cargo_metadata::{Metadata, MetadataCommand, Package};
use std::{collections::BTreeMap, path::Path};

use crate::builder_error::BuilderError;
use crate::{builder_error::BuilderError, multiple_crate_versions};

/// Helper to get a crate info extracted from the `Cargo.toml`.
#[derive(Debug, Default)]
Expand Down Expand Up @@ -52,6 +52,9 @@ impl CrateInfo {
let root_package = Self::root_package(&metadata)
.ok_or_else(|| BuilderError::RootPackageNotFound.into())
.and_then(Self::check)?;

multiple_crate_versions::check(&metadata, &root_package.id)?;

let name = root_package.name.clone();
let snake_case_name = name.replace('-', "_");
let version = root_package.version.to_string();
Expand Down
1 change: 1 addition & 0 deletions utils/wasm-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use wasm_project::ProjectType;
mod builder_error;
pub mod code_validator;
mod crate_info;
mod multiple_crate_versions;
mod smart_fs;
mod wasm_project;

Expand Down
71 changes: 71 additions & 0 deletions utils/wasm-builder/src/multiple_crate_versions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use anyhow::{Error, Result};
use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId};
use itertools::Itertools;
use std::collections::HashSet;

const DEFAULT_DENIED_DUPLICATE_CRATES: [&str; 1] = ["gstd"];
/// Returns the list of crates name.
fn denied_duplicate_crates() -> HashSet<&'static str> {
option_env!("__GEAR_WASM_BUILDER_DENIED_DUPLICATE_CRATES").map_or_else(
|| DEFAULT_DENIED_DUPLICATE_CRATES.into(),
|v| v.split(',').collect(),
)
}

pub(crate) fn check(metadata: &Metadata, root_id: &PackageId) -> Result<()> {
let denied_duplicate_crates = denied_duplicate_crates();
let mut packages = metadata.packages.clone();
packages.sort_by(|a, b| a.name.cmp(&b.name));

let mut duplicates: Vec<String> = Vec::new();

if let Some(resolve) = &metadata.resolve {
for (name, group) in &packages
.iter()
.filter(|p| denied_duplicate_crates.contains(&p.name.as_str()))
.chunk_by(|p| &p.name)
{
let group: Vec<&Package> = group.collect();

if group.len() <= 1 {
continue;
}

if group
.iter()
.all(|p| is_normal_dep(&resolve.nodes, root_id, &p.id))
{
let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect();
versions.sort();
let versions = versions.iter().join(", ");

duplicates.push(format!(
"multiple versions for dependency `{name}`: {versions}"
));
}
}
}

if duplicates.is_empty() {
Ok(())
} else {
Err(Error::msg(duplicates.join("\n")))
}
}

fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool {
fn depends_on(node: &Node, dep_id: &PackageId) -> bool {
node.deps.iter().any(|dep| {
dep.pkg == *dep_id
&& dep
.dep_kinds
.iter()
.any(|info| matches!(info.kind, DependencyKind::Normal))
})
}

nodes
.iter()
.filter(|node| depends_on(node, dep_id))
.any(|node| node.id == *local_id || is_normal_dep(nodes, local_id, &node.id))
}
16 changes: 16 additions & 0 deletions utils/wasm-builder/tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,19 @@ fn features_tracking() {
assert!(read_export_entry("handle_signal").is_some());
assert!(read_export_entry("handle_reply").is_none());
}

#[ignore]
#[test]
/// Build fails on multiple crate versions check.
/// Suppose that the `syn` crate is referenced more than once
fn build_release_for_target_deny_duplicate_crate() {
let mut cmd = CargoRunner::new()
.args(["build", "--release", "--target", TARGET])
.0;
cmd.arg("--color=always");
cmd.arg("--manifest-path=test-program/Cargo.toml");
cmd.arg("--config=env.GEAR_WASM_BUILDER_DENIED_DUPLICATE_CRATES=\'syn\'");

let status = cmd.status().expect("cargo run error");
assert!(!status.success())
}

0 comments on commit 81fe12d

Please sign in to comment.