-
Notifications
You must be signed in to change notification settings - Fork 10
/
build.rs
109 lines (89 loc) · 3.23 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use eyre::{eyre, Result};
use std::{env, fs, path::PathBuf};
use ethers::{
contract::Abigen,
solc::{ConfigurableArtifacts, Project, ProjectCompileOutput, ProjectPathsConfig, Solc},
};
use inflector::Inflector;
use semver::{Version, VersionReq};
const SOLC_VERSION_REQ: &str = "^0.8.0";
const COMPILE_PATH: &str = "test/contracts";
fn main() -> Result<()> {
if env::var_os("CARGO_FEATURE_TXGEN").is_none() {
return Ok(());
}
let contracts_to_bind = vec!["Store", "Factory"];
let base_dir = std::env::current_dir()?;
let build_dir = mkdir(base_dir.join("build"));
let bindings_dir = mkdir(base_dir.join("src/bindings"));
let output = compile(base_dir.join(COMPILE_PATH))?;
let mut bindings = String::new();
for name in contracts_to_bind {
let contract = output.find_first(name).ok_or_else(|| {
eyre!(
"Could Not bind contract {}. Compiler output not found.",
name
)
})?;
// write bytecode to build dir if binding a non-abstract contract
if let Some(bin) = &contract.bytecode {
fs::write(
&build_dir.join(name.to_snake_case().to_owned() + ".bin"),
hex::encode(bin.object.clone()),
)?;
}
// generate bindings from the abi
let abi = serde_json::to_string(
contract
.abi
.as_ref()
.expect("tried to bind a contract with no abi"),
)?;
let mod_name = name.to_snake_case();
Abigen::new(name, abi)
.map_err(|e| eyre!("new abigen failure: {}", e))?
.generate()
.map_err(|e| eyre!("abigen failure: {}", e))?
.write_to_file(bindings_dir.join(mod_name.clone() + ".rs"))
.map_err(|e| eyre!("failed to write bindings: {}", e))?;
bindings.push_str(&format!("pub mod {};\n", mod_name));
}
fs::write(bindings_dir.join("mod.rs"), bindings)?;
// Pass build_dir to env as SOLC_BUILD_DIR
println!(
"cargo:rustc-env=SOLC_BUILD_DIR={}",
build_dir.into_os_string().into_string().unwrap()
);
Ok(())
}
fn compile(dir: PathBuf) -> Result<ProjectCompileOutput<ConfigurableArtifacts>> {
let solc = Solc::default();
check_solc(solc.version().expect("No solc version"));
let paths = ProjectPathsConfig::builder().sources(dir).build()?;
let project = Project::builder()
.paths(paths)
.solc(solc)
.no_artifacts()
.build()?;
// tell cargo to rerun build script if contracts change
project.rerun_if_sources_changed();
let output = project.compile()?;
if output.has_compiler_errors() {
eyre::bail!(output.to_string())
} else {
Ok(output)
}
}
fn check_solc(version: Version) {
let req = VersionReq::parse(SOLC_VERSION_REQ).expect("Cannot parse SOLC_VERSION_REQ");
if !req.matches(&version) {
println!("cargo:warning=solc version mismatch. Using local solc executable, version: {}. Expected: {}", version, req);
}
}
fn mkdir(dir: PathBuf) -> PathBuf {
if !dir.exists() {
fs::create_dir(&dir)
.unwrap_or_else(|_| panic!("could not create dir: {}", dir.to_string_lossy()));
}
dir
}