fix: generated crate not using generated version (#1852)

* fix: generated crate not using generated version

* add docs

* wip: add base testcases

* fix: str formatting

* fix: add missing comma

* fix: tests and file loading

* fix: strip whitespaces

* fix: case where we are using specified crate

* linting: remove extra ref

* refactor: use toml parser over regex

* fix: add case for path and ethers-contract

* fix: add missing comma

* feat: don't check for path

* remove build-dep fallback
This commit is contained in:
Will Smith 2022-11-15 16:00:12 -05:00 committed by GitHub
parent 936cecc3ad
commit d47e34dc5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 227 additions and 15 deletions

View File

@ -101,7 +101,7 @@
## ethers-contract-abigen ## ethers-contract-abigen
### Unreleased ### Unreleased
- Fix Cargo.toml generation issue that could cause dependency conflicts [#1852](https://github.com/gakonst/ethers-rs/pull/1852)
- Use corresponding rust structs for event fields if they're solidity structs [#1674](https://github.com/gakonst/ethers-rs/pull/1674) - Use corresponding rust structs for event fields if they're solidity structs [#1674](https://github.com/gakonst/ethers-rs/pull/1674)
- Add `ContractFilter` to filter contracts in `MultiAbigen` [#1564](https://github.com/gakonst/ethers-rs/pull/1564) - Add `ContractFilter` to filter contracts in `MultiAbigen` [#1564](https://github.com/gakonst/ethers-rs/pull/1564)
- generate error bindings for custom errors [#1549](https://github.com/gakonst/ethers-rs/pull/1549) - generate error bindings for custom errors [#1549](https://github.com/gakonst/ethers-rs/pull/1549)

5
Cargo.lock generated
View File

@ -1278,6 +1278,7 @@ dependencies = [
"serde_json", "serde_json",
"syn", "syn",
"tempfile", "tempfile",
"toml",
"url", "url",
"walkdir", "walkdir",
] ]
@ -4100,9 +4101,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.5.8" version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [ dependencies = [
"serde", "serde",
] ]

View File

@ -29,6 +29,7 @@ dunce = "1.0.2"
walkdir = "2.3.2" walkdir = "2.3.2"
eyre = "0.6" eyre = "0.6"
regex = "1.6.0" regex = "1.6.0"
toml = "0.5.9"
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
# NOTE: this enables wasm compatibility for getrandom indirectly # NOTE: this enables wasm compatibility for getrandom indirectly

View File

@ -1,4 +1,5 @@
//! Generate bindings for multiple `Abigen` //! Generate bindings for multiple `Abigen`
use crate::{util, Abigen, Context, ContractBindings, ContractFilter, ExpandedContract};
use eyre::Result; use eyre::Result;
use inflector::Inflector; use inflector::Inflector;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
@ -7,10 +8,9 @@ use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet}, collections::{BTreeMap, BTreeSet, HashMap, HashSet},
fs, fs,
io::Write, io::Write,
path::Path, path::{Path, PathBuf},
}; };
use toml::Value;
use crate::{util, Abigen, Context, ContractBindings, ContractFilter, ExpandedContract};
/// Collects Abigen structs for a series of contracts, pending generation of /// Collects Abigen structs for a series of contracts, pending generation of
/// the contract bindings. /// the contract bindings.
@ -553,6 +553,7 @@ impl MultiBindingsInner {
&self, &self,
name: impl AsRef<str>, name: impl AsRef<str>,
version: impl AsRef<str>, version: impl AsRef<str>,
crate_version: String,
) -> Result<Vec<u8>> { ) -> Result<Vec<u8>> {
let mut toml = vec![]; let mut toml = vec![];
@ -564,15 +565,31 @@ impl MultiBindingsInner {
writeln!(toml, "# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html")?; writeln!(toml, "# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html")?;
writeln!(toml)?; writeln!(toml)?;
writeln!(toml, "[dependencies]")?; writeln!(toml, "[dependencies]")?;
writeln!( writeln!(toml, r#"{}"#, crate_version)?;
toml,
r#"
ethers = {{ git = "https://github.com/gakonst/ethers-rs", default-features = false, features = ["abigen"] }}
"#
)?;
Ok(toml) Ok(toml)
} }
/// parses the active Cargo.toml to get what version of ethers we are using
fn find_crate_version(&self) -> Result<String> {
let cargo_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
let data = std::fs::read_to_string(cargo_dir)?;
let toml = data.parse::<Value>()?;
let Some(ethers) = toml.get("dependencies")
.and_then (|v| v.get("ethers").or_else(|| v.get("ethers-contract")))
else { eyre::bail!("couldn't find ethers or ethers-contract dependency")};
if let Some(rev) = ethers.get("rev") {
Ok(format!("ethers = {{ git = \"https://github.com/gakonst/ethers-rs\", rev = {}, default-features = false, features = [\"abigen\"] }}", rev))
} else if let Some(version) = ethers.get("version") {
Ok(format!(
"ethers = {{ version = {}, default-features = false, features = [\"abigen\"] }}",
version
))
} else {
Ok("ethers = {{ git = \"https://github.com/gakonst/ethers-rs\", default-features = false, features = [\"abigen\"] }}".to_string())
}
}
/// Write the contents of `Cargo.toml` to disk /// Write the contents of `Cargo.toml` to disk
fn write_cargo_toml( fn write_cargo_toml(
&self, &self,
@ -580,7 +597,8 @@ ethers = {{ git = "https://github.com/gakonst/ethers-rs", default-features = fal
name: impl AsRef<str>, name: impl AsRef<str>,
version: impl AsRef<str>, version: impl AsRef<str>,
) -> Result<()> { ) -> Result<()> {
let contents = self.generate_cargo_toml(name, version)?; let crate_version = self.find_crate_version()?;
let contents = self.generate_cargo_toml(name, version, crate_version)?;
let mut file = fs::OpenOptions::new() let mut file = fs::OpenOptions::new()
.read(true) .read(true)
@ -717,7 +735,8 @@ ethers = {{ git = "https://github.com/gakonst/ethers-rs", default-features = fal
if check_cargo_toml { if check_cargo_toml {
// additionally check the contents of the cargo // additionally check the contents of the cargo
let cargo_contents = self.generate_cargo_toml(name, version)?; let crate_version = self.find_crate_version()?;
let cargo_contents = self.generate_cargo_toml(name, version, crate_version)?;
check_file_in_dir(crate_path, "Cargo.toml", &cargo_contents)?; check_file_in_dir(crate_path, "Cargo.toml", &cargo_contents)?;
} }
@ -775,7 +794,7 @@ mod tests {
use crate::{ExcludeContracts, SelectContracts}; use crate::{ExcludeContracts, SelectContracts};
use ethers_solc::project_util::TempProject; use ethers_solc::project_util::TempProject;
use std::{panic, path::PathBuf}; use std::{env, panic, path::PathBuf};
struct Context { struct Context {
multi_gen: MultiAbigen, multi_gen: MultiAbigen,
@ -1238,4 +1257,195 @@ contract Enum {
let content = fs::read_to_string(&mod_).unwrap(); let content = fs::read_to_string(&mod_).unwrap();
assert!(content.contains("pub mod mod_ {")); assert!(content.contains("pub mod mod_ {"));
} }
#[test]
fn parse_ethers_crate() {
// gotta bunch these all together as we are overwriting env vars
run_test(|context| {
let Context { multi_gen, mod_root } = context;
let tmp = TempProject::dapptools().unwrap();
tmp.add_source(
"Cargo.toml",
r#"
[package]
name = "ethers-contract"
version = "1.0.0"
edition = "2018"
rust-version = "1.62"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
license = "MIT OR Apache-2.0"
description = "Smart contract bindings for the ethers-rs crate"
homepage = "https://docs.rs/ethers"
repository = "https://github.com/gakonst/ethers-rs"
keywords = ["ethereum", "web3", "celo", "ethers"]
[dependencies]
ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-features = false }
"#,
)
.unwrap();
let _ = tmp.compile().unwrap();
env::set_var("CARGO_MANIFEST_DIR", tmp.root());
let single_file = false;
let name = "a-name";
let version = "290.3782.3";
multi_gen
.clone()
.build()
.unwrap()
.write_to_crate(name, version, mod_root, single_file)
.unwrap();
multi_gen
.clone()
.build()
.unwrap()
.ensure_consistent_crate(name, version, mod_root, single_file, true)
.expect("Inconsistent bindings");
});
run_test(|context| {
let Context { multi_gen, mod_root } = context;
let tmp = TempProject::dapptools().unwrap();
tmp.add_source(
"Cargo.toml",
r#"
[package]
name = "ethers-contract"
version = "1.0.0"
edition = "2018"
rust-version = "1.62"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
license = "MIT OR Apache-2.0"
description = "Smart contract bindings for the ethers-rs crate"
homepage = "https://docs.rs/ethers"
repository = "https://github.com/gakonst/ethers-rs"
keywords = ["ethereum", "web3", "celo", "ethers"]
[dependencies]
ethers-contracts = "0.4.0"
"#,
)
.unwrap();
let _ = tmp.compile().unwrap();
env::set_var("CARGO_MANIFEST_DIR", tmp.root());
let single_file = false;
let name = "a-name";
let version = "290.3782.3";
multi_gen
.clone()
.build()
.unwrap()
.write_to_crate(name, version, mod_root, single_file)
.unwrap();
multi_gen
.clone()
.build()
.unwrap()
.ensure_consistent_crate(name, version, mod_root, single_file, true)
.expect("Inconsistent bindings");
});
run_test(|context| {
let Context { multi_gen, mod_root } = context;
let tmp = TempProject::dapptools().unwrap();
tmp.add_source(
"Cargo.toml",
r#"
[package]
name = "ethers-contract"
version = "1.0.0"
edition = "2018"
rust-version = "1.62"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
license = "MIT OR Apache-2.0"
description = "Smart contract bindings for the ethers-rs crate"
homepage = "https://docs.rs/ethers"
repository = "https://github.com/gakonst/ethers-rs"
keywords = ["ethereum", "web3", "celo", "ethers"]
[dependencies]
ethers = {git="https://github.com/gakonst/ethers-rs", rev = "fd8ebf5",features = ["ws", "rustls", "ipc"] }
"#,
)
.unwrap();
let _ = tmp.compile().unwrap();
env::set_var("CARGO_MANIFEST_DIR", tmp.root());
let single_file = false;
let name = "a-name";
let version = "290.3782.3";
multi_gen
.clone()
.build()
.unwrap()
.write_to_crate(name, version, mod_root, single_file)
.unwrap();
multi_gen
.clone()
.build()
.unwrap()
.ensure_consistent_crate(name, version, mod_root, single_file, true)
.expect("Inconsistent bindings");
});
run_test(|context| {
let Context { multi_gen, mod_root } = context;
let tmp = TempProject::dapptools().unwrap();
tmp.add_source(
"Cargo.toml",
r#"
[package]
name = "ethers-contract"
version = "1.0.0"
edition = "2018"
rust-version = "1.62"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
license = "MIT OR Apache-2.0"
description = "Smart contract bindings for the ethers-rs crate"
homepage = "https://docs.rs/ethers"
repository = "https://github.com/gakonst/ethers-rs"
keywords = ["ethereum", "web3", "celo", "ethers"]
[dependencies]
ethers = {git="https://github.com/gakonst/ethers-rs" ,features = ["ws", "rustls", "ipc"] }
"#,
)
.unwrap();
let _ = tmp.compile().unwrap();
env::set_var("CARGO_MANIFEST_DIR", tmp.root());
let single_file = false;
let name = "a-name";
let version = "290.3782.3";
multi_gen
.clone()
.build()
.unwrap()
.write_to_crate(name, version, mod_root, single_file)
.unwrap();
multi_gen
.clone()
.build()
.unwrap()
.ensure_consistent_crate(name, version, mod_root, single_file, true)
.expect("Inconsistent bindings");
});
}
} }