diff --git a/treasury-factory/build.rs b/treasury-factory/build.rs index de757353..a8c831fa 100644 --- a/treasury-factory/build.rs +++ b/treasury-factory/build.rs @@ -1,10 +1,62 @@ use base64::{engine::general_purpose, Engine as _}; -use cargo_near_build::extended::*; use cargo_near_build::BuildOpts; +use cargo_near_build::{bon, camino, extended}; use std::env; use std::fs; use std::io::Write; use std::path::Path; +use std::str::FromStr; + +struct ChildContract { + workdir: &'static str, + manifest: camino::Utf8PathBuf, + nep330_contract_path: &'static str, +} + +impl ChildContract { + fn get() -> Self { + // unix path to target sub-contract's crate from root of the repo + let nep330_contract_path = "web4/treasury-web4"; + let workdir = "../web4/treasury-web4"; + let manifest = camino::Utf8PathBuf::from_str(workdir) + .expect("pathbuf from str") + .join("Cargo.toml"); + + Self { + workdir, + nep330_contract_path, + manifest, + } + } + + // returns absolute path, relative to parent contract + fn sub_build_target_dir() -> String { + let path = Path::new("./target/build-rs-treasury-web4-for-treasury-factory"); + + std::fs::create_dir_all(&path).unwrap_or_else(|err| panic!( + "create sub-build target dir {}: {:#?}", path.to_string_lossy(), err + )); + + let path = path.canonicalize() + .unwrap_or_else(|err| panic!("canonicalize() path: {:#?}", err)); + + path.to_str() + .unwrap_or_else(|| panic!("valid unicode path expected {}", path.to_string_lossy())) + .to_owned() + } + + // by the way, BASE64("") = "" + const STUB_PATH: &str = "./target/treasury-web4-stub.bin"; + + fn intermediate_result_env_key() -> &'static str { + "BUILD_RS_SUB_BUILD_ARTIFACT_RAW_WASM" + } + + const ENCODED_RESULT_PATH: &str = "./target/treasury_web4.wasm.base64.txt"; + fn final_result_env_key() -> &'static str { + "BUILD_RS_SUB_BUILD_ARTIFACT_BASE64_ENCODED_WASM" + } +} fn main() { // Change working directory to the directory of the script (similar to process.chdir) @@ -30,32 +82,96 @@ fn main() { .write_all(index_html_base64.as_bytes()) .expect("Failed to write to output file"); - let web4_wasm_path = "../web4/treasury-web4/target/near/treasury_web4.wasm"; - if !fs::exists(web4_wasm_path).unwrap() { + let child_contract = ChildContract::get(); + let build_artifact = { let build_opts = BuildOpts::builder() - .manifest_path("../web4/treasury-web4/Cargo.toml".into()) + .manifest_path(child_contract.manifest) + .override_nep330_contract_path(child_contract.nep330_contract_path) + .override_cargo_target_dir(ChildContract::sub_build_target_dir()) .build(); - let build_script_opts = BuildScriptOpts::builder().build(); - let build_opts_extended = BuildOptsExtended::builder() + let build_script_opts = extended::BuildScriptOpts::builder() + .rerun_if_changed_list(bon::vec![child_contract.workdir]) + .build_skipped_when_env_is(vec![ + // shorter build for `cargo check` + ("PROFILE", "debug"), + (cargo_near_build::env_keys::BUILD_RS_ABI_STEP_HINT, "true"), + ]) + .stub_path(ChildContract::STUB_PATH) + .result_env_key(ChildContract::intermediate_result_env_key()) + .build(); + + let build_opts_extended = extended::BuildOptsExtended::builder() .build_opts(build_opts) .build_script_opts(build_script_opts) .build(); - let build_artifact = build(build_opts_extended).expect("Building web4 contract failed"); + extended::build(build_opts_extended).unwrap_or_else(|err| { + panic!( + "Building `{}` contract failed: {:#?}", + child_contract.workdir, err + ) + }) + }; + + let web4_wasm_base64_path = base64_encode_wasm(&build_artifact); - let web4_wasm = fs::read(build_artifact.path) - .expect(format!("Failed to read {}", web4_wasm_path).as_str()); + export_result(web4_wasm_base64_path); +} - let web4_wasm_base64 = general_purpose::STANDARD.encode(&web4_wasm); +fn base64_encode_wasm(build_artifact: &cargo_near_build::BuildArtifact) -> std::path::PathBuf { + let web4_wasm_base64 = { + let web4_wasm = fs::read(&build_artifact.path) + .unwrap_or_else(|err| panic!("Failed to read {:?}: {:#?}", build_artifact.path, err)); + general_purpose::STANDARD.encode(&web4_wasm) + }; - let web4_wasm_base64_path = - Path::new(env!("CARGO_MANIFEST_DIR")).join("treasury_web4.wasm.base64.txt"); + let web4_wasm_base64_path = Path::new(ChildContract::ENCODED_RESULT_PATH); - let mut output_file = - fs::File::create(web4_wasm_base64_path).expect("Failed to create output file"); + let mut output_file = + fs::File::create(web4_wasm_base64_path).unwrap_or_else(|err| { + panic!( + "Failed to create output file {:?}, {:#?}", + web4_wasm_base64_path, err + ) + }); - output_file - .write_all(web4_wasm_base64.as_bytes()) - .expect("Failed to write to output file"); - } + output_file + .write_all(web4_wasm_base64.as_bytes()) + .unwrap_or_else(|err| { + panic!( + "Failed to write to output file {:?}, {:#?}", + web4_wasm_base64_path, err + ) + }); + web4_wasm_base64_path + .canonicalize() + .unwrap_or_else(|err| panic!("canonicalize() path: {:#?}", err)) +} + +fn export_result(web4_wasm_base64_path: std::path::PathBuf) { + // this function doesn't return result + // result becomes available to crate's code (src/lib.rs) + // via `cargo::rustc-env` export of `result_env_key` variable + let _result_env_key = { + let result_env_key = ChildContract::final_result_env_key(); + + let path = web4_wasm_base64_path.to_str().unwrap_or_else(|| { + panic!( + "valid unicode path expected {}", + web4_wasm_base64_path.to_string_lossy() + ) + }); + println!( + "cargo::warning={}", + format!("Path to base64 encoded artifact: {}", path,) + ); + println!( + "cargo::warning={}", + format!( + "Path to base64-encoded wasm artifact is exported to `{}`", + result_env_key, + ) + ); + println!("cargo::rustc-env={}={}", result_env_key, path); + }; } diff --git a/treasury-factory/src/lib.rs b/treasury-factory/src/lib.rs index b996d8b6..c3437520 100644 --- a/treasury-factory/src/lib.rs +++ b/treasury-factory/src/lib.rs @@ -43,7 +43,7 @@ impl Contract { "new_account_id": new_instance_contract_id.clone(), "options": { "full_access_keys": [env::signer_account_pk(),admin_full_access_public_key], - "contract_bytes_base64": include_str!("../treasury_web4.wasm.base64.txt") + "contract_bytes_base64": include_str!(env!("BUILD_RS_SUB_BUILD_ARTIFACT_BASE64_ENCODED_WASM")) } }) .to_string()