From 634877a6764e07b562cbba923bca11e8856a5c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= <26653921+dj8yfo@users.noreply.github.com> Date: Sat, 19 Oct 2024 00:39:22 +0300 Subject: [PATCH] chore!: remove unsafe `std::env::set_var` (#228) --- .github/workflows/test.yml | 7 +- Cargo.lock | 52 ++++----- cargo-near-build/Cargo.toml | 18 ++- cargo-near-build/src/cargo_native/compile.rs | 16 +-- cargo-near-build/src/env_keys.rs | 11 +- cargo-near-build/src/lib.rs | 70 +++++++----- cargo-near-build/src/near/abi/mod.rs | 7 +- cargo-near-build/src/near/build/export.rs | 32 ------ cargo-near-build/src/near/build/mod.rs | 79 ++++++++----- .../src/near/build_extended/build_script.rs | 32 ++---- .../src/near/build_extended/mod.rs | 88 ++++++++------- .../src/near/build_extended/tmp_env.rs | 106 ------------------ cargo-near-build/src/near/docker_build/mod.rs | 14 +-- .../src/near/docker_build/subprocess_step.rs | 9 +- cargo-near-build/src/types/cargo/metadata.rs | 16 ++- cargo-near-build/src/types/near/abi.rs | 2 +- .../buildtime_env/abi_builder_version.rs | 32 ++++++ .../near/build/buildtime_env/abi_path.rs | 23 ++++ .../types/near/build/buildtime_env/command.rs | 43 +++++++ .../types/near/build/buildtime_env/link.rs | 22 ++++ .../src/types/near/build/buildtime_env/mod.rs | 73 ++++++++++++ .../overrides/cargo_target_dir.rs | 19 ++++ .../near/build/buildtime_env/overrides/mod.rs | 2 + .../buildtime_env/overrides/nep330_path.rs | 17 +++ .../types/near/build/buildtime_env/version.rs | 16 +++ .../src/types/near/build/input/mod.rs | 37 ++++-- cargo-near-build/src/types/near/build/mod.rs | 1 + .../src/types/near/build/output/mod.rs | 5 +- .../types/near/build/output/version_info.rs | 96 ++++++++++++++++ .../near/build/output/version_mismatch.rs | 84 -------------- .../types/near/build_extended/build_script.rs | 43 ++++--- .../src/types/near/build_extended/mod.rs | 9 +- .../types/near/docker_build/cloned_repo.rs | 10 +- .../compute_command.rs} | 25 ++--- .../src/types/near/docker_build/metadata.rs | 9 ++ .../src/types/near/docker_build/mod.rs | 60 +++++++++- cargo-near/src/commands/build_command/mod.rs | 32 +++++- cargo-near/src/commands/mod.rs | 2 +- integration-tests/Cargo.toml | 6 +- integration-tests/src/lib.rs | 80 ++++++++----- integration-tests/tests/abi/borsh_schema.rs | 5 +- integration-tests/tests/abi/opts.rs | 27 +++-- integration-tests/tests/build/opts.rs | 3 +- integration-tests/tests/docker.rs | 4 +- 44 files changed, 827 insertions(+), 517 deletions(-) delete mode 100644 cargo-near-build/src/near/build/export.rs delete mode 100644 cargo-near-build/src/near/build_extended/tmp_env.rs create mode 100644 cargo-near-build/src/types/near/build/buildtime_env/abi_builder_version.rs create mode 100644 cargo-near-build/src/types/near/build/buildtime_env/abi_path.rs create mode 100644 cargo-near-build/src/types/near/build/buildtime_env/command.rs create mode 100644 cargo-near-build/src/types/near/build/buildtime_env/link.rs create mode 100644 cargo-near-build/src/types/near/build/buildtime_env/mod.rs create mode 100644 cargo-near-build/src/types/near/build/buildtime_env/overrides/cargo_target_dir.rs create mode 100644 cargo-near-build/src/types/near/build/buildtime_env/overrides/mod.rs create mode 100644 cargo-near-build/src/types/near/build/buildtime_env/overrides/nep330_path.rs create mode 100644 cargo-near-build/src/types/near/build/buildtime_env/version.rs create mode 100644 cargo-near-build/src/types/near/build/output/version_info.rs delete mode 100644 cargo-near-build/src/types/near/build/output/version_mismatch.rs rename cargo-near-build/src/types/near/{build/input/docker_context.rs => docker_build/compute_command.rs} (88%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index faeaae07..d7b1d1b5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -97,8 +97,13 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install packages (Linux) + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install --assume-yes libudev-dev - name: run cargo doc - run: RUSTDOCFLAGS="-D warnings" cargo doc -p cargo-near-build --features build_script + run: | + RUSTDOCFLAGS="-D warnings" cargo doc --workspace --features cargo-near-build/build_script + cargo test --workspace --features cargo-near-build/build_script --doc audit: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 4d18d3ca..8baf5e9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -311,6 +311,29 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bon" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97493a391b4b18ee918675fb8663e53646fd09321c58b46afa04e8ce2499c869" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2af3eac944c12cdf4423eab70d310da0a8e5851a18ffb192c0a5e3f7ae1663" +dependencies = [ + "darling", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "borsh" version = "1.5.1" @@ -508,6 +531,7 @@ dependencies = [ name = "cargo-near-build" version = "0.2.0" dependencies = [ + "bon", "bs58 0.5.1", "camino", "cargo_metadata", @@ -546,11 +570,10 @@ dependencies = [ "cargo-near", "cargo-near-build 0.2.0", "color-eyre", + "colored", "const_format", - "env_logger", "function_name", "git2", - "log", "minifier", "near-workspaces", "prettyplease 0.2.22", @@ -560,6 +583,8 @@ dependencies = [ "syn 2.0.77", "tempfile", "tokio", + "tracing", + "tracing-subscriber", "zstd 0.13.2", ] @@ -1277,29 +1302,6 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "env_filter" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", -] - [[package]] name = "equivalent" version = "1.0.1" diff --git a/cargo-near-build/Cargo.toml b/cargo-near-build/Cargo.toml index e0b3eddc..8efa34a4 100644 --- a/cargo-near-build/Cargo.toml +++ b/cargo-near-build/Cargo.toml @@ -23,6 +23,7 @@ near-abi = { version = "0.4.0", features = ["__chunked-entries"] } zstd = "0.13" schemars = "0.8" rustc_version = "0.4" +bon = "2.3.0" url = { version = "2.5.0", features = ["serde"], optional = true } serde = { version = "1.0.197", optional = true } git2 = { version = "0.19", optional = true } @@ -30,7 +31,7 @@ home = { version = "0.5.9", optional = true } pathdiff = { version = "0.2.1", features = ["camino"], optional = true } unix_path = { version = "1.0.1", optional = true } tempfile = { version = "3.10.1", optional = true } -shell-words = { version = "1.0.0", optional = true} +shell-words = { version = "1.0.0", optional = true } wasm-opt = "=0.116.1" humantime = "2.1.0" @@ -47,10 +48,15 @@ dist = false [features] default = [] build_script = [] -abi_build = [] +abi_build = [] docker = [ - "dep:url", "dep:serde", "dep:git2", - "dep:home", "dep:pathdiff", "dep:unix_path", - "dep:tempfile", "dep:nix", "dep:shell-words" + "dep:url", + "dep:serde", + "dep:git2", + "dep:home", + "dep:pathdiff", + "dep:unix_path", + "dep:tempfile", + "dep:nix", + "dep:shell-words", ] - diff --git a/cargo-near-build/src/cargo_native/compile.rs b/cargo-near-build/src/cargo_native/compile.rs index c5f8f67f..bfaaa647 100644 --- a/cargo-near-build/src/cargo_native/compile.rs +++ b/cargo-near-build/src/cargo_native/compile.rs @@ -6,10 +6,7 @@ use eyre::{ContextCompat, WrapErr}; use std::io::BufRead; use crate::types::near::build::input::ColorPreference; -use crate::types::{ - cargo::manifest_path::ManifestPath, - near::build::{output::version_mismatch::VersionMismatch, output::CompilationArtifact}, -}; +use crate::types::{cargo::manifest_path::ManifestPath, near::build::output::CompilationArtifact}; use super::ArtifactType; @@ -36,10 +33,13 @@ where let rustflags: &mut String = final_env .entry(key) .or_insert_with(|| std::env::var(key).unwrap_or_default()); - if !rustflags.is_empty() { - rustflags.push(' '); + // helps avoids situation on complete match `RUSTFLAGS="-C link-arg=-s -C link-arg=-s"` + if !rustflags.contains(value) { + if !rustflags.is_empty() { + rustflags.push(' '); + } + rustflags.push_str(value); } - rustflags.push_str(value); } _ => { final_env.insert(key, value.to_string()); @@ -84,7 +84,7 @@ where path, fresh: !compile_artifact.fresh, from_docker: false, - builder_version_mismatch: VersionMismatch::None, + builder_version_info: None, artifact_type: PhantomData, }), _ => eyre::bail!( diff --git a/cargo-near-build/src/env_keys.rs b/cargo-near-build/src/env_keys.rs index a925758a..b764f7f9 100644 --- a/cargo-near-build/src/env_keys.rs +++ b/cargo-near-build/src/env_keys.rs @@ -1,6 +1,10 @@ +/// this is [`CARGO_TARGET_DIR`](https://doc.rust-lang.org/cargo/reference/environment-variables.html) +pub const CARGO_TARGET_DIR: &str = "CARGO_TARGET_DIR"; /// this variable is set to `"true"` during ABI generation operation pub const BUILD_RS_ABI_STEP_HINT: &str = "CARGO_NEAR_ABI_GENERATION"; +pub(crate) const CARGO_NEAR_ABI_PATH: &str = "CARGO_NEAR_ABI_PATH"; + pub(crate) const CARGO_NEAR_VERSION: &str = "CARGO_NEAR_VERSION"; pub(crate) const CARGO_NEAR_ABI_SCHEMA_VERSION: &str = "CARGO_NEAR_ABI_SCHEMA_VERSION"; @@ -32,12 +36,7 @@ pub mod nep330 { pub(crate) fn print_env() { tracing::info!("Variables, relevant for reproducible builds:"); - for key in [ - BUILD_ENVIRONMENT, - BUILD_COMMAND, - CONTRACT_PATH, - SOURCE_CODE_SNAPSHOT, - ] { + for key in [BUILD_ENVIRONMENT, CONTRACT_PATH, SOURCE_CODE_SNAPSHOT] { let value = std::env::var(key) .map(|val| format!("'{}'", val)) .unwrap_or("unset".to_string()); diff --git a/cargo-near-build/src/lib.rs b/cargo-near-build/src/lib.rs index cb501897..719c4c6d 100644 --- a/cargo-near-build/src/lib.rs +++ b/cargo-near-build/src/lib.rs @@ -16,6 +16,7 @@ //! //! 1. [camino] is re-exported, because it is used in [BuildOpts], and [BuildArtifact] as type of some of fields //! 2. [near_abi] is re-exported, because details of ABI generated depends on specific version of `near-abi` dependency +//! 3. [bon] is re-exported for the convenience of [bon::vec] helper macro //! //! ## Sample usage: //! @@ -28,11 +29,8 @@ //! With some options set: //! //! ```no_run -//! let build_opts = cargo_near_build::BuildOpts { -//! features: Some("some_contract_feature_1".into()), -//! ..Default::default() -//! }; -//! let artifact = cargo_near_build::build(build_opts).expect("some error during build"); +//! let build_opts = cargo_near_build::BuildOpts::builder().features("some_contract_feature_1").build(); +//! let artifact = cargo_near_build::build(build_opts).expect("some error during build"); //! ``` pub(crate) mod cargo_native; /// module contains names of environment variables, exported during @@ -67,39 +65,54 @@ pub use build_exports::*; /// Potential import may look like this: /// ```ignore /// [build-dependencies.cargo-near-build] -/// version = "0.1.0" +/// version = "x.y.z" /// features = ["build_script"] /// ``` /// /// Usage example: /// /// ```no_run -/// use cargo_near_build::extended::BuildScriptOpts; -/// let opts = cargo_near_build::extended::BuildOptsExtended { -/// workdir: "../another-contract", -/// env: vec![ -/// // unix path of target contract from root of repo -/// (cargo_near_build::env_keys::nep330::CONTRACT_PATH, "another-contract") -/// ], -/// build_opts: cargo_near_build::BuildOpts::default(), -/// build_script_opts: BuildScriptOpts { -/// result_env_key: Some("BUILD_RS_SUB_BUILD_ARTIFACT_1"), -/// rerun_if_changed_list: vec!["../another-contract", "../Cargo.toml", "../Cargo.lock"], -/// build_skipped_when_env_is: vec![ -/// // shorter build for `cargo check` -/// ("PROFILE", "debug"), -/// (cargo_near_build::env_keys::BUILD_RS_ABI_STEP_HINT, "true"), -/// ], -/// distinct_target_dir: Some("../target/build-rs-another-contract"), -/// stub_path: Some("../target/stub.bin"), -/// }, -/// }; -/// cargo_near_build::extended::build(opts).expect("sub-contract build error"); +/// use cargo_near_build::{bon, extended}; +/// use cargo_near_build::BuildOpts; +/// use std::str::FromStr; +/// +/// // directory of target sub-contract's crate +/// let workdir = "../another-contract"; +/// // unix path to target sub-contract's crate from root of the repo +/// let nep330_contract_path = "another-contract"; +/// let manifest = camino::Utf8PathBuf::from_str(workdir) +/// .expect("pathbuf from str") +/// .join("Cargo.toml"); +/// +/// let build_opts = BuildOpts::builder() +/// .manifest_path(manifest) +/// .override_nep330_contract_path(nep330_contract_path) +/// // a distinct target is needed to avoid deadlock during build +/// .override_cargo_target_dir("../target/build-rs-another-contract") +/// .build(); // default opts +/// +/// let build_script_opts = extended::BuildScriptOpts::builder() +/// .rerun_if_changed_list(bon::vec![workdir, "../Cargo.toml", "../Cargo.lock",]) +/// .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("../target/stub.bin") +/// .result_env_key("BUILD_RS_SUB_BUILD_ARTIFACT_1") +/// .build(); +/// +/// let extended_opts = extended::BuildOptsExtended::builder() +/// .build_opts(build_opts) +/// .build_script_opts(build_script_opts) +/// .build(); +/// +/// cargo_near_build::extended::build(extended_opts).expect("sub-contract build error"); /// ``` #[cfg(feature = "build_script")] pub mod extended { pub use crate::near::build_extended::run as build; - pub use crate::types::near::build_extended::build_script::Opts as BuildScriptOpts; + pub use crate::types::near::build_extended::build_script::{EnvPairs, Opts as BuildScriptOpts}; pub use crate::types::near::build_extended::OptsExtended as BuildOptsExtended; } @@ -109,5 +122,6 @@ pub mod docker { pub use crate::types::near::docker_build::Opts as DockerBuildOpts; } +pub use bon; pub use camino; pub use near_abi; diff --git a/cargo-near-build/src/near/abi/mod.rs b/cargo-near-build/src/near/abi/mod.rs index 0898a298..814d4bcf 100644 --- a/cargo-near-build/src/near/abi/mod.rs +++ b/cargo-near-build/src/near/abi/mod.rs @@ -4,7 +4,7 @@ use crate::types::near::abi as abi_types; pub mod generate; #[cfg(feature = "abi_build")] -pub fn build(args: abi_types::Opts) -> eyre::Result<()> { +pub fn build(args: abi_types::Opts) -> eyre::Result { // imports #[cfg(feature = "abi_build")] use crate::{ pretty_print, @@ -22,7 +22,8 @@ pub fn build(args: abi_types::Opts) -> eyre::Result<()> { } else { "Cargo.toml".into() }; - CrateMetadata::collect(ManifestPath::try_from(manifest_path)?, args.no_locked) + let manifest_path = ManifestPath::try_from(manifest_path)?; + CrateMetadata::collect(manifest_path, args.no_locked, None) })?; let out_dir = crate_metadata.resolve_output_dir(args.out_dir.map(Into::into))?; @@ -53,7 +54,7 @@ pub fn build(args: abi_types::Opts) -> eyre::Result<()> { pretty_print::success("ABI Successfully Generated!"); eprintln!(" - ABI: {}", abi_path.to_string().yellow().bold()); - Ok(()) + Ok(abi_path) } pub fn write_to_file( diff --git a/cargo-near-build/src/near/build/export.rs b/cargo-near-build/src/near/build/export.rs deleted file mode 100644 index 8362bcf3..00000000 --- a/cargo-near-build/src/near/build/export.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::{env_keys, types::near::build::input::Opts}; - -pub fn nep_330_build_command(args: &Opts) -> eyre::Result<()> { - tracing::debug!( - "compute `CARGO_NEAR_BUILD_COMMAND`, current executable: {:?}", - std::env::args().collect::>() - ); - let env_value: Vec = match std::env::args().next() { - // this is for cli context, being called from `cargo-near` bin - Some(cli_arg_0) - if cli_arg_0.ends_with("cargo-near") || cli_arg_0.ends_with("cargo-near.exe") => - { - let mut cmd: Vec = vec!["cargo".into()]; - // skipping `cargo-near` - cmd.extend(std::env::args().skip(1)); - cmd - } - // this is for lib context, when build method is called from code - // where `cargo-near` is an unlikely name to be chosen for executable - _ => { - // NOTE: order of output of cli flags shouldn't be too important, as the version of - // `cargo-near` used as lib will be fixed in `Cargo.lock` - args.get_cli_build_command() - } - }; - - std::env::set_var( - env_keys::nep330::BUILD_COMMAND, - serde_json::to_string(&env_value)?, - ); - Ok(()) -} diff --git a/cargo-near-build/src/near/build/mod.rs b/cargo-near-build/src/near/build/mod.rs index 53a2334c..585671d4 100644 --- a/cargo-near-build/src/near/build/mod.rs +++ b/cargo-near-build/src/near/build/mod.rs @@ -1,5 +1,6 @@ use crate::cargo_native::Wasm; use crate::types::near::abi as abi_types; +use crate::types::near::build::buildtime_env; use camino::Utf8PathBuf; use colored::Colorize; use near_abi::BuildInfo; @@ -15,19 +16,18 @@ use crate::{ manifest_path::{ManifestPath, MANIFEST_FILE_NAME}, metadata::CrateMetadata, }, - near::build::{input::Opts, output::version_mismatch::VersionMismatch}, + near::build::{input::Opts, output::version_info::VersionInfo}, }, }; use super::abi; -pub mod export; - /// builds a contract whose crate root is current workdir, or identified by [`Cargo.toml`/BuildOpts::manifest_path](crate::BuildOpts::manifest_path) location pub fn run(args: Opts) -> eyre::Result { let start = std::time::Instant::now(); - VersionMismatch::export_builder_and_near_abi_versions(); - export::nep_330_build_command(&args)?; + let override_cargo_target_path_env = + buildtime_env::CargoTargetDir::maybe_new(args.override_cargo_target_dir.clone()); + env_keys::nep330::print_env(); let color = args.color.unwrap_or(ColorPreference::Auto); @@ -41,22 +41,21 @@ pub fn run(args: Opts) -> eyre::Result { })?; let crate_metadata = pretty_print::handle_step("Collecting cargo project metadata...", || { - let manifest_path: Utf8PathBuf = if let Some(manifest_path) = args.manifest_path { + let manifest_path: Utf8PathBuf = if let Some(manifest_path) = args.manifest_path.clone() { manifest_path } else { MANIFEST_FILE_NAME.into() }; - CrateMetadata::collect(ManifestPath::try_from(manifest_path)?, args.no_locked) + let manifest_path = ManifestPath::try_from(manifest_path)?; + CrateMetadata::collect( + manifest_path, + args.no_locked, + override_cargo_target_path_env.as_ref(), + ) })?; - let out_dir = crate_metadata.resolve_output_dir(args.out_dir.map(Into::into))?; + let out_dir = crate_metadata.resolve_output_dir(args.out_dir.clone().map(Into::into))?; - let mut build_env = vec![("RUSTFLAGS", "-C link-arg=-s")]; - build_env.extend( - args.env - .iter() - .map(|(key, value)| (key.as_ref(), value.as_ref())), - ); let mut cargo_args = vec!["--target", COMPILATION_TARGET]; let cargo_feature_args = { let mut feat_args = vec![]; @@ -79,30 +78,43 @@ pub fn run(args: Opts) -> eyre::Result { let mut abi = None; let mut min_abi_path = None; - let (builder_version, builder_version_mismatch) = - VersionMismatch::get_coerced_builder_version()?; + let builder_version_info = VersionInfo::get_coerced_builder_version()?; + + let common_vars_env = buildtime_env::CommonVariables::new( + &args, + &builder_version_info, + &crate_metadata, + override_cargo_target_path_env, + )?; + if !args.no_abi { let mut contract_abi = { - let env = args + let mut abi_env = args .env .iter() .map(|(key, value)| (key.as_ref(), value.as_ref())) .collect::>(); + common_vars_env.append_borrowed_to(&mut abi_env); + abi::generate::procedure( &crate_metadata, args.no_locked, !args.no_doc, true, &cargo_feature_args, - &env, - color.clone(), + &abi_env, + color, )? }; let embedding_binary = args.cli_description.cli_name_abi; contract_abi.metadata.build = Some(BuildInfo { compiler: format!("rustc {}", rustc_version::version()?), - builder: format!("{} {}", embedding_binary, builder_version), + builder: format!( + "{} {}", + embedding_binary, + builder_version_info.result_builder_version()? + ), image: None, }); if !args.no_embed_abi { @@ -122,20 +134,25 @@ pub fn run(args: Opts) -> eyre::Result { cargo_args.extend(cargo_feature_args); - if let (false, Some(abi_path)) = (args.no_embed_abi, &min_abi_path) { + if let (false, Some(..)) = (args.no_embed_abi, &min_abi_path) { cargo_args.extend(&["--features", "near-sdk/__abi-embed"]); - build_env.push(("CARGO_NEAR_ABI_PATH", abi_path.as_str())); } - let version = crate_metadata.root_package.version.to_string(); - build_env.push((env_keys::nep330::VERSION, &version)); - // this will be set in docker builds (externally to current process), having more info about git commit - if std::env::var(env_keys::nep330::LINK).is_err() { - if let Some(ref repository) = crate_metadata.root_package.repository { - build_env.push((env_keys::nep330::LINK, repository)); - } - } + let abi_path_env = buildtime_env::AbiPath::new(args.no_embed_abi, &min_abi_path); + + let build_env = { + let mut build_env = vec![("RUSTFLAGS", "-C link-arg=-s")]; + build_env.extend( + args.env + .iter() + .map(|(key, value)| (key.as_ref(), value.as_ref())), + ); + + abi_path_env.append_borrowed_to(&mut build_env); + common_vars_env.append_borrowed_to(&mut build_env); + build_env + }; pretty_print::step("Building contract"); let mut wasm_artifact = cargo_native::compile::run::( &crate_metadata.manifest_path, @@ -159,7 +176,7 @@ pub fn run(args: Opts) -> eyre::Result { )?; } - wasm_artifact.builder_version_mismatch = builder_version_mismatch; + wasm_artifact.builder_version_info = Some(builder_version_info); // todo! if we embedded, check that the binary exports the __contract_abi symbol diff --git a/cargo-near-build/src/near/build_extended/build_script.rs b/cargo-near-build/src/near/build_extended/build_script.rs index a5ebc408..5886747e 100644 --- a/cargo-near-build/src/near/build_extended/build_script.rs +++ b/cargo-near-build/src/near/build_extended/build_script.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use crate::types::near::build::output::version_mismatch::VersionMismatch; +use crate::types::near::build::output::version_info::VersionInfo; use crate::types::near::build::output::CompilationArtifact; use crate::types::near::build_extended::build_script::Opts; use rustc_version::Version; @@ -9,21 +9,10 @@ use rustc_version::Version; /// was implemented since this version const DEPRECATE_SINGLE_COLON_SINCE: Version = Version::new(1, 77, 0); -macro_rules! print_warn { - ($version: ident, $($tokens: tt)*) => { - let separator = if $version >= &DEPRECATE_SINGLE_COLON_SINCE { - "::" - } else { - ":" - }; - println!("cargo{}warning={}", separator, format!($($tokens)*)) - } -} - -impl<'a> Opts<'a> { +impl Opts { pub(crate) fn should_skip(&self, version: &Version) -> bool { let mut return_bool = false; - for (env_key, value_to_skip) in self.build_skipped_when_env_is.iter() { + for (env_key, value_to_skip) in self.build_skipped_when_env_is.0.iter() { if let Ok(actual_value) = std::env::var(env_key) { if actual_value == *value_to_skip { return_bool = true; @@ -59,7 +48,7 @@ impl<'a> Opts<'a> { path: stub_path, fresh: true, from_docker: false, - builder_version_mismatch: VersionMismatch::None, + builder_version_info: None, artifact_type: PhantomData, } }; @@ -67,10 +56,9 @@ impl<'a> Opts<'a> { } pub(crate) fn post_build( - &self, + self, skipped: bool, artifact: &CompilationArtifact, - workdir: &str, version: &Version, ) -> Result<(), Box> { let colon_separator = if version >= &DEPRECATE_SINGLE_COLON_SINCE { @@ -78,13 +66,14 @@ impl<'a> Opts<'a> { } else { ":" }; - if let ref version_mismatch @ VersionMismatch::Some { .. } = - artifact.builder_version_mismatch + if let ref version_info @ Some(VersionInfo::EnvMismatch { .. }) = + artifact.builder_version_info { + let version_info = version_info.as_ref().unwrap(); print_warn!( version, "INFO: `cargo-near` version was coerced during build: {}.", - version_mismatch + version_info ); print_warn!(version, "`cargo-near` crate version (used in `build.rs`) did not match `cargo-near` build environment."); print_warn!(version, "You may consider to optionally make 2 following versions match exactly, if they're too far away:"); @@ -107,8 +96,7 @@ impl<'a> Opts<'a> { ); print_warn!( version, - "Path to result artifact of build in `{}` is exported to `{}`", - workdir, + "Path to result artifact of build is exported to `{}`", result_env_key, ); } diff --git a/cargo-near-build/src/near/build_extended/mod.rs b/cargo-near-build/src/near/build_extended/mod.rs index d7d9a14a..c3f40cca 100644 --- a/cargo-near-build/src/near/build_extended/mod.rs +++ b/cargo-near-build/src/near/build_extended/mod.rs @@ -1,54 +1,58 @@ +macro_rules! print_warn { + ($version: expr, $($tokens: tt)*) => { + let separator = if $version >= &DEPRECATE_SINGLE_COLON_SINCE { + "::" + } else { + ":" + }; + println!("cargo{}warning={}", separator, format!($($tokens)*)) + } +} + +/// `cargo::` prefix for build script outputs, that `cargo` recognizes +/// was implemented since this version +const DEPRECATE_SINGLE_COLON_SINCE: Version = Version::new(1, 77, 0); + mod build_script; -mod tmp_env; + use crate::types::near::build::output::CompilationArtifact; use crate::types::near::build_extended::OptsExtended; +use crate::BuildOpts; use rustc_version::Version; +use crate::extended::BuildScriptOpts; + pub fn run(args: OptsExtended) -> Result> { let actual_version = rustc_version::version()?; - let (artifact, skipped) = args.skip_or_compile(&actual_version)?; + print_warn!( + &actual_version, + "build script of `{}` is happening in workdir: {:?}", + std::env::var("CARGO_PKG_NAME").unwrap_or("unset CARGO_PKG_NAME".into()), + std::env::current_dir() + .map(|path| path.to_string_lossy().into_owned()) + .unwrap_or("ERR GET PWD".into()) + ); + let OptsExtended { + build_opts, + build_script_opts, + } = args; + let (artifact, skipped) = skip_or_compile(build_opts, &build_script_opts, &actual_version)?; - args.build_script_opts - .post_build(skipped, &artifact, args.workdir, &actual_version)?; + build_script_opts.post_build(skipped, &artifact, &actual_version)?; Ok(artifact) } -impl<'a> OptsExtended<'a> { - pub(crate) fn skip_or_compile( - &self, - version: &Version, - ) -> Result<(CompilationArtifact, bool), Box> { - let _tmp_workdir = tmp_env::set_current_dir(self.workdir)?; - let result = if self.build_script_opts.should_skip(version) { - let artifact = self.build_script_opts.create_empty_stub()?; - (artifact, true) - } else { - let artifact = self.compile_near_artifact()?; - (artifact, false) - }; - Ok(result) - } - - /// `CARGO_TARGET_DIR` export is needed to avoid attempt to acquire same `target//.cargo-lock` - /// as the `cargo` process, which is running the build-script - pub(crate) fn compile_near_artifact( - &self, - ) -> Result> { - let mut tmp_envs = vec![]; - for (env_key, value) in self.env.iter() { - let tmp_env = tmp_env::set_var(env_key, value); - tmp_envs.push(tmp_env); - } - - let artifact = if let Some(distinct_target_dir) = self.build_script_opts.distinct_target_dir - { - let _tmp_cargo_target_env = tmp_env::set_var("CARGO_TARGET_DIR", distinct_target_dir); - - crate::build(self.build_opts.clone())? - } else { - crate::build(self.build_opts.clone())? - }; - - Ok(artifact) - } +pub(crate) fn skip_or_compile( + build_opts: BuildOpts, + build_script_opts: &BuildScriptOpts, + version: &Version, +) -> Result<(CompilationArtifact, bool), Box> { + let result = if build_script_opts.should_skip(version) { + let artifact = build_script_opts.create_empty_stub()?; + (artifact, true) + } else { + let artifact = crate::build(build_opts)?; + (artifact, false) + }; + Ok(result) } diff --git a/cargo-near-build/src/near/build_extended/tmp_env.rs b/cargo-near-build/src/near/build_extended/tmp_env.rs deleted file mode 100644 index 52457f74..00000000 --- a/cargo-near-build/src/near/build_extended/tmp_env.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::ffi::{OsStr, OsString}; -use std::fmt::Debug; -use std::path::Path; - -/// Memorize the current path and switch to the given path. Once the datastructure is -/// dropped, switch back to the original path automatically. -pub fn set_current_dir>(path: P) -> Result { - let current_dir = std::env::current_dir()?; - std::env::set_current_dir(&path)?; - Ok(CurrentDir(current_dir)) -} - -/// A helper datastructure for ensuring that we switch back to the current folder before the -/// end of the current scope. -pub struct CurrentDir(std::path::PathBuf); - -impl Debug for CurrentDir { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl Drop for CurrentDir { - fn drop(&mut self) { - std::env::set_current_dir(&self.0).expect("cannot go back to the previous directory"); - } -} - -/// Sets the environment variable k to the value v for the currently running process. -/// It returns a datastructure to keep the environment variable set. When dropped the environment variable is restored -pub fn set_var, V: AsRef>(key: K, value: V) -> CurrentEnv { - let key = key.as_ref(); - let previous_val = std::env::var(key).ok(); - std::env::set_var(key, value); - CurrentEnv(key.to_owned(), previous_val) -} - -/// A helper datastructure for ensuring that we restore the current environment variable before the -/// end of the current scope. -pub struct CurrentEnv(OsString, Option); - -impl Debug for CurrentEnv { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl Drop for CurrentEnv { - fn drop(&mut self) { - match self.1.take() { - Some(previous_val) => std::env::set_var(&self.0, previous_val), - None => std::env::remove_var(&self.0), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_current_dir() { - println!( - "current dir: {:?}", - std::env::current_dir().expect("patronus") - ); - { - let _tmp_current_dir = set_current_dir("src").expect("should set the new current_dir"); - let current_dir = std::env::current_dir().expect("cannot get current dir from std env"); - assert!(current_dir.ends_with("src")); - } - let current_dir = std::env::current_dir().expect("cannot get current dir from std env"); - assert!(!current_dir.ends_with("src")); - // Because guard is dropped - set_current_dir("src/near").expect("should set the new current_dir"); - assert!(!current_dir.ends_with("src/near")); - } - - #[test] - fn test_env() { - { - let _tmp_env = set_var("TEST_TMP_ENV", "myvalue"); - assert_eq!(std::env::var("TEST_TMP_ENV"), Ok(String::from("myvalue"))); - } - assert!(std::env::var("TEST_TMP_ENV").is_err()); - // Because guard is dropped - set_var("TEST_TMP_ENV_DROPPED", "myvaluedropped"); - assert!(std::env::var("TEST_TMP_ENV_DROPPED").is_err()); - } - - #[test] - fn test_env_with_previous_value() { - std::env::set_var("TEST_TMP_ENV_PREVIOUS", "previous_value"); - { - let _tmp_env = set_var("TEST_TMP_ENV_PREVIOUS", "myvalue"); - assert_eq!( - std::env::var("TEST_TMP_ENV_PREVIOUS"), - Ok(String::from("myvalue")) - ); - } - assert_eq!( - std::env::var("TEST_TMP_ENV_PREVIOUS"), - Ok(String::from("previous_value")) - ); - } -} diff --git a/cargo-near-build/src/near/docker_build/mod.rs b/cargo-near-build/src/near/docker_build/mod.rs index 1a029da6..cf47bbaf 100644 --- a/cargo-near-build/src/near/docker_build/mod.rs +++ b/cargo-near-build/src/near/docker_build/mod.rs @@ -1,9 +1,10 @@ use std::process::{Command, ExitStatus}; +use crate::docker::DockerBuildOpts; use crate::types::near::build::input::BuildContext; use crate::types::near::build::output::CompilationArtifact; use crate::types::near::docker_build::{cloned_repo, crate_in_repo, metadata}; -use crate::{docker::DockerBuildOpts, env_keys, pretty_print}; +use crate::{env_keys, pretty_print}; pub mod docker_checks; pub mod git_checks; @@ -11,16 +12,15 @@ pub mod subprocess_step; pub const ERR_REPRODUCIBLE: &str = "Reproducible build in docker container failed."; -pub fn run(docker_opts: DockerBuildOpts) -> eyre::Result { - let opts = docker_opts.build_opts; - let color = opts.color.clone().unwrap_or(crate::ColorPreference::Auto); +pub fn run(opts: DockerBuildOpts) -> eyre::Result { + let color = opts.color.unwrap_or(crate::ColorPreference::Auto); color.apply(); let crate_in_repo = pretty_print::handle_step( "Opening repo and determining HEAD and relative path of contract...", || crate_in_repo::Crate::find(&opts.contract_path()?), )?; pretty_print::handle_step("Checking if git is dirty...", || { - git_checks::dirty::check_then_handle(docker_opts.context, &crate_in_repo.repo_root) + git_checks::dirty::check_then_handle(opts.context, &crate_in_repo.repo_root) })?; let cloned_repo = pretty_print::handle_step( "Cloning project repo to a temporary build site, removing uncommitted changes...", @@ -28,7 +28,7 @@ pub fn run(docker_opts: DockerBuildOpts) -> eyre::Result { cloned_repo::ClonedRepo::check_locked_then_clone( crate_in_repo.clone(), opts.no_locked, - docker_opts.context, + opts.context, ) }, )?; @@ -40,7 +40,7 @@ pub fn run(docker_opts: DockerBuildOpts) -> eyre::Result { if let BuildContext::Deploy { skip_git_remote_check, - } = docker_opts.context + } = opts.context { if !skip_git_remote_check { pretty_print::handle_step( diff --git a/cargo-near-build/src/near/docker_build/subprocess_step.rs b/cargo-near-build/src/near/docker_build/subprocess_step.rs index 4b05385d..34441d79 100644 --- a/cargo-near-build/src/near/docker_build/subprocess_step.rs +++ b/cargo-near-build/src/near/docker_build/subprocess_step.rs @@ -1,4 +1,5 @@ use super::docker_checks; +use crate::docker::DockerBuildOpts; use colored::Colorize; use std::io::IsTerminal; use std::{ @@ -12,10 +13,9 @@ use nix::unistd::{getgid, getuid}; use crate::env_keys; use crate::types::near::docker_build::subprocess::{container_paths, env_vars}; use crate::types::near::docker_build::{cloned_repo, metadata}; -use crate::BuildOpts; pub fn run( - opts: BuildOpts, + opts: DockerBuildOpts, docker_build_meta: metadata::ReproducibleBuild, cloned_repo: &cloned_repo::ClonedRepo, ) -> eyre::Result<(ExitStatus, Command)> { @@ -47,10 +47,7 @@ pub fn run( let env = env_vars::EnvVars::new(&docker_build_meta, cloned_repo)?; let env_args = env.docker_args(); let shell_escaped_cargo_cmd = { - let cargo_cmd = opts.get_cli_build_command_in_docker( - docker_build_meta.container_build_command.clone(), - docker_build_meta.passed_env.clone(), - )?; + let cargo_cmd = opts.get_cli_build_command_in_docker(&docker_build_meta)?; tracing::debug!("cli_build_command_in_docker {:#?}", cargo_cmd); shell_words::join(cargo_cmd) }; diff --git a/cargo-near-build/src/types/cargo/metadata.rs b/cargo-near-build/src/types/cargo/metadata.rs index ea633876..08bcf5ff 100644 --- a/cargo-near-build/src/types/cargo/metadata.rs +++ b/cargo-near-build/src/types/cargo/metadata.rs @@ -5,6 +5,8 @@ use cargo_metadata::{MetadataCommand, Package}; use colored::Colorize; use eyre::{ContextCompat, WrapErr}; +use crate::types::near::build::buildtime_env; + use super::manifest_path::ManifestPath; /// Relevant metadata obtained from Cargo.toml. @@ -18,8 +20,13 @@ pub struct CrateMetadata { impl CrateMetadata { /// Parses the contract manifest and returns relevant metadata. - pub fn collect(manifest_path: ManifestPath, no_locked: bool) -> eyre::Result { - let (mut metadata, root_package) = get_cargo_metadata(&manifest_path, no_locked)?; + pub fn collect( + manifest_path: ManifestPath, + no_locked: bool, + cargo_target_dir: Option<&buildtime_env::CargoTargetDir>, + ) -> eyre::Result { + let (mut metadata, root_package) = + get_cargo_metadata(&manifest_path, no_locked, cargo_target_dir)?; metadata.target_directory = crate::fs::force_canonicalize_dir(&metadata.target_directory)?; metadata.workspace_root = metadata.workspace_root.canonicalize_utf8()?; @@ -70,12 +77,17 @@ impl CrateMetadata { fn get_cargo_metadata( manifest_path: &ManifestPath, no_locked: bool, + cargo_target_dir: Option<&buildtime_env::CargoTargetDir>, ) -> eyre::Result<(cargo_metadata::Metadata, Package)> { tracing::info!("Fetching cargo metadata for {}", manifest_path.path); let mut cmd = MetadataCommand::new(); if !no_locked { cmd.other_options(["--locked".to_string()]); } + if let Some(target_dir) = cargo_target_dir { + let (key, value) = target_dir.entry(); + cmd.env(key, value); + } let cmd = cmd.manifest_path(&manifest_path.path); tracing::debug!("metadata command: {:#?}", cmd.cargo_command()); let metadata = cmd.exec(); diff --git a/cargo-near-build/src/types/near/abi.rs b/cargo-near-build/src/types/near/abi.rs index 8fdab65e..3aeaa410 100644 --- a/cargo-near-build/src/types/near/abi.rs +++ b/cargo-near-build/src/types/near/abi.rs @@ -4,7 +4,7 @@ use camino::Utf8PathBuf; use crate::types::near::build::input::ColorPreference; #[cfg(feature = "abi_build")] -#[derive(Default)] +#[derive(Debug, Default)] pub struct Opts { /// disable implicit `--locked` flag for all `cargo` commands, enabled by default pub no_locked: bool, diff --git a/cargo-near-build/src/types/near/build/buildtime_env/abi_builder_version.rs b/cargo-near-build/src/types/near/build/buildtime_env/abi_builder_version.rs new file mode 100644 index 00000000..b65ac596 --- /dev/null +++ b/cargo-near-build/src/types/near/build/buildtime_env/abi_builder_version.rs @@ -0,0 +1,32 @@ +use crate::env_keys; + +pub struct BuilderAbiVersions { + builder_version: String, + near_abi_schema_version: String, +} + +impl BuilderAbiVersions { + pub fn new(builder_version: String, near_abi_schema_version: String) -> Self { + Self { + builder_version, + near_abi_schema_version, + } + } + pub fn builder_version_env_key() -> &'static str { + env_keys::CARGO_NEAR_VERSION + } + pub fn abi_schema_version_env_key() -> &'static str { + env_keys::CARGO_NEAR_ABI_SCHEMA_VERSION + } + + pub fn append_borrowed_to<'a>(&'a self, env: &mut Vec<(&str, &'a str)>) { + env.push(( + Self::builder_version_env_key(), + self.builder_version.as_str(), + )); + env.push(( + Self::abi_schema_version_env_key(), + self.near_abi_schema_version.as_str(), + )); + } +} diff --git a/cargo-near-build/src/types/near/build/buildtime_env/abi_path.rs b/cargo-near-build/src/types/near/build/buildtime_env/abi_path.rs new file mode 100644 index 00000000..c35638be --- /dev/null +++ b/cargo-near-build/src/types/near/build/buildtime_env/abi_path.rs @@ -0,0 +1,23 @@ +use crate::env_keys; + +pub struct AbiPath { + path: Option, +} + +impl AbiPath { + pub fn new(no_embed_abi: bool, min_abi_path: &Option) -> Self { + if !no_embed_abi { + Self { + path: min_abi_path.clone(), + } + } else { + Self { path: None } + } + } + + pub fn append_borrowed_to<'a>(&'a self, env: &mut Vec<(&str, &'a str)>) { + if let Some(ref path) = self.path { + env.push((env_keys::CARGO_NEAR_ABI_PATH, path.as_str())); + } + } +} diff --git a/cargo-near-build/src/types/near/build/buildtime_env/command.rs b/cargo-near-build/src/types/near/build/buildtime_env/command.rs new file mode 100644 index 00000000..812db3d2 --- /dev/null +++ b/cargo-near-build/src/types/near/build/buildtime_env/command.rs @@ -0,0 +1,43 @@ +use crate::env_keys; +use crate::types::near::build::input::Opts; + +pub struct Nep330BuildCommand { + value: String, +} + +impl Nep330BuildCommand { + pub fn compute(args: &Opts) -> eyre::Result { + tracing::debug!( + "compute `CARGO_NEAR_BUILD_COMMAND`, current executable: {:?}", + std::env::args().collect::>() + ); + let env_value: Vec = match std::env::args().next() { + // this is for cli context, being called from `cargo-near` bin + Some(cli_arg_0) + if cli_arg_0.ends_with("cargo-near") || cli_arg_0.ends_with("cargo-near.exe") => + { + let mut cmd: Vec = vec!["cargo".into()]; + // skipping `cargo-near` + cmd.extend(std::env::args().skip(1)); + cmd + } + // this is for lib context, when build method is called from code + // where `cargo-near` is an unlikely name to be chosen for executable + _ => { + // NOTE: order of output of cli flags shouldn't be too important, as the version of + // `cargo-near` used as lib will be fixed in `Cargo.lock` + args.get_cli_command_for_lib_context() + } + }; + + let command = Self::new(serde_json::to_string(&env_value)?); + Ok(command) + } + fn new(value: String) -> Self { + tracing::info!("{}={}", env_keys::nep330::BUILD_COMMAND, value); + Self { value } + } + pub fn append_borrowed_to<'a>(&'a self, env: &mut Vec<(&str, &'a str)>) { + env.push((env_keys::nep330::BUILD_COMMAND, self.value.as_str())); + } +} diff --git a/cargo-near-build/src/types/near/build/buildtime_env/link.rs b/cargo-near-build/src/types/near/build/buildtime_env/link.rs new file mode 100644 index 00000000..5a4fb38f --- /dev/null +++ b/cargo-near-build/src/types/near/build/buildtime_env/link.rs @@ -0,0 +1,22 @@ +use crate::{env_keys, types::cargo::metadata::CrateMetadata}; + +pub struct Nep330Link { + link: Option, +} + +impl Nep330Link { + pub fn new(crate_metadata: &CrateMetadata) -> Self { + Self { + link: crate_metadata.root_package.repository.clone(), + } + } + + pub fn append_borrowed_to<'a>(&'a self, env: &mut Vec<(&str, &'a str)>) { + // this will be set in docker builds (externally to current process), having more info about git commit + if std::env::var(env_keys::nep330::LINK).is_err() { + if let Some(ref link) = self.link { + env.push((env_keys::nep330::LINK, link.as_str())); + } + } + } +} diff --git a/cargo-near-build/src/types/near/build/buildtime_env/mod.rs b/cargo-near-build/src/types/near/build/buildtime_env/mod.rs new file mode 100644 index 00000000..ad5df907 --- /dev/null +++ b/cargo-near-build/src/types/near/build/buildtime_env/mod.rs @@ -0,0 +1,73 @@ +mod abi_path; + +mod link; +mod version; + +mod command; + +mod abi_builder_version; + +mod overrides; + +pub use abi_path::AbiPath; + +pub use link::Nep330Link; +pub use version::Nep330Version; + +pub use command::Nep330BuildCommand; + +pub use abi_builder_version::BuilderAbiVersions; + +pub use overrides::cargo_target_dir::CargoTargetDir; +pub use overrides::nep330_path::Nep330ContractPath; + +use crate::types::cargo::metadata::CrateMetadata; +use crate::BuildOpts; + +use super::output::version_info::VersionInfo; + +/// varibles, common for both steps of build, abi-gen and wasm build +pub struct CommonVariables { + pub nep330_version: Nep330Version, + pub nep330_link: Nep330Link, + pub nep330_build_cmd: Nep330BuildCommand, + pub builder_abi_versions: BuilderAbiVersions, + pub override_nep330_contract_path: Nep330ContractPath, + pub override_cargo_target_path: Option, +} + +impl CommonVariables { + pub fn new( + opts: &BuildOpts, + builder_version_info: &VersionInfo, + crate_metadata: &CrateMetadata, + override_cargo_target_path: Option, + ) -> eyre::Result { + let nep330_version = Nep330Version::new(crate_metadata); + let nep330_link = Nep330Link::new(crate_metadata); + + let nep330_build_cmd = Nep330BuildCommand::compute(opts)?; + let builder_abi_versions = builder_version_info.compute_env_variables()?; + let override_nep330_contract_path = + Nep330ContractPath::maybe_new(opts.override_nep330_contract_path.clone()); + let result = Self { + nep330_version, + nep330_link, + nep330_build_cmd, + builder_abi_versions, + override_nep330_contract_path, + override_cargo_target_path, + }; + Ok(result) + } + pub fn append_borrowed_to<'a>(&'a self, env: &mut Vec<(&str, &'a str)>) { + self.nep330_version.append_borrowed_to(env); + self.nep330_link.append_borrowed_to(env); + self.nep330_build_cmd.append_borrowed_to(env); + self.builder_abi_versions.append_borrowed_to(env); + self.override_nep330_contract_path.append_borrowed_to(env); + if let Some(ref target_dir) = self.override_cargo_target_path { + target_dir.append_borrowed_to(env); + } + } +} diff --git a/cargo-near-build/src/types/near/build/buildtime_env/overrides/cargo_target_dir.rs b/cargo-near-build/src/types/near/build/buildtime_env/overrides/cargo_target_dir.rs new file mode 100644 index 00000000..e05bccd9 --- /dev/null +++ b/cargo-near-build/src/types/near/build/buildtime_env/overrides/cargo_target_dir.rs @@ -0,0 +1,19 @@ +use crate::env_keys; + +pub struct CargoTargetDir { + path: String, +} + +impl CargoTargetDir { + pub fn maybe_new(path: Option) -> Option { + path.map(|path| Self { path }) + } + + pub fn entry(&self) -> (&'static str, &str) { + (env_keys::CARGO_TARGET_DIR, self.path.as_str()) + } + + pub fn append_borrowed_to<'a>(&'a self, env: &mut Vec<(&str, &'a str)>) { + env.push(self.entry()); + } +} diff --git a/cargo-near-build/src/types/near/build/buildtime_env/overrides/mod.rs b/cargo-near-build/src/types/near/build/buildtime_env/overrides/mod.rs new file mode 100644 index 00000000..ad17087c --- /dev/null +++ b/cargo-near-build/src/types/near/build/buildtime_env/overrides/mod.rs @@ -0,0 +1,2 @@ +pub(super) mod cargo_target_dir; +pub(super) mod nep330_path; diff --git a/cargo-near-build/src/types/near/build/buildtime_env/overrides/nep330_path.rs b/cargo-near-build/src/types/near/build/buildtime_env/overrides/nep330_path.rs new file mode 100644 index 00000000..7b850fb8 --- /dev/null +++ b/cargo-near-build/src/types/near/build/buildtime_env/overrides/nep330_path.rs @@ -0,0 +1,17 @@ +use crate::env_keys; + +pub struct Nep330ContractPath { + path: Option, +} + +impl Nep330ContractPath { + pub fn maybe_new(path: Option) -> Self { + Self { path } + } + + pub fn append_borrowed_to<'a>(&'a self, env: &mut Vec<(&str, &'a str)>) { + if let Some(ref path) = self.path { + env.push((env_keys::nep330::CONTRACT_PATH, path.as_str())); + } + } +} diff --git a/cargo-near-build/src/types/near/build/buildtime_env/version.rs b/cargo-near-build/src/types/near/build/buildtime_env/version.rs new file mode 100644 index 00000000..eba69c49 --- /dev/null +++ b/cargo-near-build/src/types/near/build/buildtime_env/version.rs @@ -0,0 +1,16 @@ +use crate::{env_keys, types::cargo::metadata::CrateMetadata}; + +pub struct Nep330Version { + version: String, +} + +impl Nep330Version { + pub fn new(crate_metadata: &CrateMetadata) -> Self { + Self { + version: crate_metadata.root_package.version.to_string(), + } + } + pub fn append_borrowed_to<'a>(&'a self, env: &mut Vec<(&str, &'a str)>) { + env.push((env_keys::nep330::VERSION, self.version.as_str())); + } +} diff --git a/cargo-near-build/src/types/near/build/input/mod.rs b/cargo-near-build/src/types/near/build/input/mod.rs index c9fb7a29..ba60b733 100644 --- a/cargo-near-build/src/types/near/build/input/mod.rs +++ b/cargo-near-build/src/types/near/build/input/mod.rs @@ -1,9 +1,6 @@ use std::env; use std::io::IsTerminal; -#[cfg(feature = "docker")] -mod docker_context; - #[cfg(feature = "docker")] #[derive(Debug, Clone, Copy)] pub enum BuildContext { @@ -18,37 +15,60 @@ pub enum BuildContext { /// - `None` - for `Option`-s /// - empty vector - for `Vec` /// - delegates to [impl Default for CliDescription](struct.CliDescription.html#impl-Default-for-CliDescription) -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, bon::Builder)] pub struct Opts { /// disable implicit `--locked` flag for all `cargo` commands, enabled by default + #[builder(default)] pub no_locked: bool, /// Build contract in debug mode, without optimizations and bigger in size + #[builder(default)] pub no_release: bool, /// Do not generate ABI for the contract + #[builder(default)] pub no_abi: bool, /// Do not embed the ABI in the contract binary + #[builder(default)] pub no_embed_abi: bool, /// Do not include rustdocs in the embedded ABI + #[builder(default)] pub no_doc: bool, /// do not run `wasm-opt -O` on the generated output as a post-step + #[builder(default)] pub no_wasmopt: bool, /// Copy final artifacts to this directory pub out_dir: Option, /// Path to the `Cargo.toml` of the contract to build pub manifest_path: Option, /// Set compile-time feature flags. + #[builder(into)] pub features: Option, /// Disables default feature flags. + #[builder(default)] pub no_default_features: bool, /// Coloring: auto, always, never; /// assumed to be auto when `None` pub color: Option, /// description of cli command, where [BuildOpts](crate::BuildOpts) are being used from, either real /// or emulated + #[builder(default)] pub cli_description: CliDescription, /// additional environment key-value pairs, that should be passed to underlying /// build commands + #[builder(default)] pub env: Vec<(String, String)>, + /// override value of [crate::env_keys::nep330::CONTRACT_PATH] environment variable, + /// needed in context of [crate::extended::build] logic, when a sub-contract being built inside of `build.rs` + /// resides in different [crate::env_keys::nep330::CONTRACT_PATH] than the current contract + #[builder(into)] + pub override_nep330_contract_path: Option, + /// override value of [crate::env_keys::CARGO_TARGET_DIR] environment variable, + /// which is required to avoid deadlock in context of [crate::extended::build] logic + /// when a sub-contract is built in `build.rs` + /// + /// should best be a subfolder of [crate::env_keys::CARGO_TARGET_DIR] + /// of crate being built to work normally + #[builder(into)] + pub override_cargo_target_dir: Option, } /// used as field in [BuildOpts](crate::BuildOpts) @@ -77,7 +97,7 @@ impl Opts { /// this is just 1-to-1 mapping of each struct's field to a cli flag /// in order of fields, as specified in struct's definition. /// `Default` implementation corresponds to plain `cargo near build` command without any args - pub(crate) fn get_cli_build_command(&self) -> Vec { + pub(crate) fn get_cli_command_for_lib_context(&self) -> Vec { let cargo_args = self.cli_description.cli_command_prefix.clone(); let mut cargo_args: Vec<&str> = cargo_args.iter().map(|ele| ele.as_str()).collect(); if self.no_locked { @@ -102,9 +122,6 @@ impl Opts { if let Some(ref out_dir) = self.out_dir { cargo_args.extend_from_slice(&["--out-dir", out_dir.as_str()]); } - if let Some(ref manifest_path) = self.manifest_path { - cargo_args.extend_from_slice(&["--manifest-path", manifest_path.as_str()]); - } if let Some(ref features) = self.features { cargo_args.extend(&["--features", features]); } @@ -141,7 +158,7 @@ impl Opts { /// /// otherwise it's [ColorPreference::Always] if stderr is a terminal device, /// and [ColorPreference::Never] in the remaining cases -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum ColorPreference { Auto, Always, @@ -200,7 +217,7 @@ mod tests { ..Default::default() }; - assert_eq!(opts.get_cli_build_command(), ["cargo".to_string(), + assert_eq!(opts.get_cli_command_for_lib_context(), ["cargo".to_string(), "near".to_string(), "build".to_string(), "--env".to_string(), diff --git a/cargo-near-build/src/types/near/build/mod.rs b/cargo-near-build/src/types/near/build/mod.rs index 0bdea7d5..386f909e 100644 --- a/cargo-near-build/src/types/near/build/mod.rs +++ b/cargo-near-build/src/types/near/build/mod.rs @@ -1,3 +1,4 @@ +pub mod buildtime_env; pub mod input; pub mod output; pub mod side_effects; diff --git a/cargo-near-build/src/types/near/build/output/mod.rs b/cargo-near-build/src/types/near/build/output/mod.rs index 8c221539..25c6d17a 100644 --- a/cargo-near-build/src/types/near/build/output/mod.rs +++ b/cargo-near-build/src/types/near/build/output/mod.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use crate::cargo_native::{ArtifactType, Wasm}; use camino::Utf8PathBuf; use sha2::{Digest, Sha256}; -pub mod version_mismatch; +pub mod version_info; /// type of success value of result of [build](crate::build) function pub struct CompilationArtifact { @@ -12,7 +12,8 @@ pub struct CompilationArtifact { pub fresh: bool, /// whether the artifact file originated from docker build or regular build with rust toolchain pub from_docker: bool, - pub(crate) builder_version_mismatch: version_mismatch::VersionMismatch, + /// `None` of `Option` means it hasn't been set yet + pub(crate) builder_version_info: Option, pub(crate) artifact_type: PhantomData, } diff --git a/cargo-near-build/src/types/near/build/output/version_info.rs b/cargo-near-build/src/types/near/build/output/version_info.rs new file mode 100644 index 00000000..4423b4c2 --- /dev/null +++ b/cargo-near-build/src/types/near/build/output/version_info.rs @@ -0,0 +1,96 @@ +use crate::types::near::build::buildtime_env::BuilderAbiVersions; + +pub type Version = String; + +#[derive(Debug)] +pub(crate) enum VersionInfo { + EnvMismatch { + environment: Version, + current_process: Version, + }, + CurrentProcess(Version), + #[cfg(feature = "docker")] + UnknownFromDocker, +} + +impl std::fmt::Display for VersionInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::EnvMismatch { + environment, + current_process, + } => { + write!( + f, + "builder version `{}` -> builder environment version `{}`", + current_process, environment + ) + } + Self::CurrentProcess { .. } => write!(f, "no `cargo-near` version mismatch in nested builds detected",), + #[cfg(feature = "docker")] + Self::UnknownFromDocker => write!(f, "it's unknown if `cargo-near` version mismatch has occurred in docker build environment",), + } + } +} + +impl VersionInfo { + pub fn result_builder_version(&self) -> eyre::Result { + match self { + Self::EnvMismatch { environment, .. } => Ok(environment.clone()), + Self::CurrentProcess(current_process) => Ok(current_process.clone()), + #[cfg(feature = "docker")] + Self::UnknownFromDocker => Err(eyre::eyre!( + "info about Version mismatch is unknown \ + for docker build", + )), + } + } + + fn current_builder_version() -> Version { + format!("{} {}", "cargo-near-build", env!("CARGO_PKG_VERSION")) + } + + fn current_near_abi_schema_version() -> Version { + near_abi::SCHEMA_VERSION.into() + } + pub fn compute_env_variables(&self) -> eyre::Result { + let builder_version = self.result_builder_version()?; + let v = BuilderAbiVersions::new(builder_version, Self::current_near_abi_schema_version()); + Ok(v) + } + + pub fn get_coerced_builder_version() -> eyre::Result { + Self::check_near_abi_schema_mismatch()?; + + let current_process_version = Self::current_builder_version(); + let result = match std::env::var(BuilderAbiVersions::builder_version_env_key()) { + Err(_err) => VersionInfo::CurrentProcess(current_process_version), + Ok(env_version) => match env_version == current_process_version { + true => VersionInfo::CurrentProcess(current_process_version), + // coercing to env_version on mismatch + false => VersionInfo::EnvMismatch { + environment: env_version, + current_process: current_process_version, + }, + }, + }; + Ok(result) + } + + fn check_near_abi_schema_mismatch() -> eyre::Result<()> { + match std::env::var(BuilderAbiVersions::abi_schema_version_env_key()) { + Ok(env_near_abi_schema_version) => { + if env_near_abi_schema_version != Self::current_near_abi_schema_version() { + Err(eyre::eyre!( + "current process NEAR_ABI_SCHEMA_VERSION mismatch with env value: {} vs {}", + Self::current_near_abi_schema_version(), + env_near_abi_schema_version, + )) + } else { + Ok(()) + } + } + Err(_err) => Ok(()), + } + } +} diff --git a/cargo-near-build/src/types/near/build/output/version_mismatch.rs b/cargo-near-build/src/types/near/build/output/version_mismatch.rs deleted file mode 100644 index 87ed4f42..00000000 --- a/cargo-near-build/src/types/near/build/output/version_mismatch.rs +++ /dev/null @@ -1,84 +0,0 @@ -use crate::env_keys; - -pub type Version = String; - -#[derive(Debug)] -pub(crate) enum VersionMismatch { - Some { - environment: Version, - current_process: Version, - }, - None, - #[cfg(feature = "docker")] - UnknownFromDocker, -} - -impl std::fmt::Display for VersionMismatch { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Some { - environment, - current_process, - } => { - write!( - f, - "builder version `{}` -> builder environment version `{}`", - current_process, environment - ) - } - Self::None => write!(f, "no `cargo-near` version mismatch in nested builds detected",), - #[cfg(feature = "docker")] - Self::UnknownFromDocker => write!(f, "it's unknown if `cargo-near` version mismatch has occurred in docker build environment",), - } - } -} - -impl VersionMismatch { - fn current_version() -> Version { - format!("{} {}", "cargo-near-build", env!("CARGO_PKG_VERSION")) - } - - pub(crate) fn export_builder_and_near_abi_versions() { - if std::env::var(env_keys::CARGO_NEAR_VERSION).is_err() { - std::env::set_var(env_keys::CARGO_NEAR_VERSION, Self::current_version()); - } - if std::env::var(env_keys::CARGO_NEAR_ABI_SCHEMA_VERSION).is_err() { - std::env::set_var( - env_keys::CARGO_NEAR_ABI_SCHEMA_VERSION, - near_abi::SCHEMA_VERSION, - ); - } - } - - pub(crate) fn get_coerced_builder_version() -> eyre::Result<(Version, VersionMismatch)> { - match std::env::var(env_keys::CARGO_NEAR_ABI_SCHEMA_VERSION) { - Ok(env_near_abi_schema_version) => { - if env_near_abi_schema_version != near_abi::SCHEMA_VERSION { - return Err(eyre::eyre!( - "current process NEAR_ABI_SCHEMA_VERSION mismatch with env value: {} vs {}", - near_abi::SCHEMA_VERSION, - env_near_abi_schema_version, - )); - } - } - Err(_err) => {} - } - let current_version = Self::current_version(); - - let result = match std::env::var(env_keys::CARGO_NEAR_VERSION) { - Err(_err) => (current_version.to_string(), VersionMismatch::None), - Ok(env_version) => match env_version == current_version { - true => (current_version.to_string(), VersionMismatch::None), - // coercing to env_version on mismatch - false => ( - env_version.clone(), - VersionMismatch::Some { - environment: env_version, - current_process: current_version.to_string(), - }, - ), - }, - }; - Ok(result) - } -} diff --git a/cargo-near-build/src/types/near/build_extended/build_script.rs b/cargo-near-build/src/types/near/build_extended/build_script.rs index af92ab8e..0649c73e 100644 --- a/cargo-near-build/src/types/near/build_extended/build_script.rs +++ b/cargo-near-build/src/types/near/build_extended/build_script.rs @@ -1,29 +1,42 @@ -#[derive(Debug, Clone)] -pub struct Opts<'a> { +#[derive(Debug, Clone, bon::Builder)] +pub struct Opts { /// environment variable name to export result `*.wasm` path to with [`cargo::rustc-env=`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-env) /// instruction - pub result_env_key: Option<&'a str>, + #[builder(into)] + pub result_env_key: Option, /// list of paths for [`cargo::rerun-if-changed=`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) /// instruction /// /// if relative, it's relative to path of crate, where build.rs is compiled - pub rerun_if_changed_list: Vec<&'a str>, + #[builder(default)] + pub rerun_if_changed_list: Vec, /// vector of key-value pairs of environment variable name and its value, /// when compilation should be skipped on a variable's value match; + /// /// e.g. /// skipping emitting output `*.wasm` may be helpful when `PROFILE` is equal to `debug` /// for using `rust-analyzer/flycheck`, `cargo check`, `bacon` and other dev-tools - pub build_skipped_when_env_is: Vec<(&'a str, &'a str)>, + #[builder(default, into)] + pub build_skipped_when_env_is: EnvPairs, /// path of stub file, where a placeholder empty `wasm` output is emitted to, when /// build is skipped due to match in [`Self::build_skipped_when_env_is`] - /// - /// if this path is relative, then the base is [`crate::extended::BuildOptsExtended::workdir`] - pub stub_path: Option<&'a str>, - /// substitution export of [`CARGO_TARGET_DIR`](https://doc.rust-lang.org/cargo/reference/environment-variables.html), - /// which is required to avoid deadlock ; - /// should best be a subfolder of [`CARGO_TARGET_DIR`](https://doc.rust-lang.org/cargo/reference/environment-variables.html) - /// of crate being built to work normally in docker builds - /// - /// if this path is relative, then the base is [`crate::extended::BuildOptsExtended::workdir`] - pub distinct_target_dir: Option<&'a str>, + #[builder(into)] + pub stub_path: Option, +} + +/// utility type which can be initialized with vector of 2-element tuples of literal strings, +/// by using [core::convert::Into] +/// like so: `vec![("key1", "value1"), ("key2", "value2")].into()` +#[derive(Default, Debug, Clone)] +pub struct EnvPairs(pub Vec<(String, String)>); + +impl From> for EnvPairs { + fn from(value: Vec<(&str, &str)>) -> Self { + let vector = value + .into_iter() + .map(|(key, value)| (key.into(), value.into())) + .collect(); + + Self(vector) + } } diff --git a/cargo-near-build/src/types/near/build_extended/mod.rs b/cargo-near-build/src/types/near/build_extended/mod.rs index 67c8c99c..7ac37d2b 100644 --- a/cargo-near-build/src/types/near/build_extended/mod.rs +++ b/cargo-near-build/src/types/near/build_extended/mod.rs @@ -3,11 +3,8 @@ use crate::BuildOpts; pub mod build_script; -#[derive(Debug, Clone)] -pub struct OptsExtended<'a> { - pub workdir: &'a str, - /// vector of key-value pairs of temporary env overrides during build process - pub env: Vec<(&'a str, &'a str)>, +#[derive(Debug, Clone, bon::Builder)] +pub struct OptsExtended { pub build_opts: BuildOpts, - pub build_script_opts: BuildScriptOpts<'a>, + pub build_script_opts: BuildScriptOpts, } diff --git a/cargo-near-build/src/types/near/docker_build/cloned_repo.rs b/cargo-near-build/src/types/near/docker_build/cloned_repo.rs index a3c076ae..8b13dd76 100644 --- a/cargo-near-build/src/types/near/docker_build/cloned_repo.rs +++ b/cargo-near-build/src/types/near/docker_build/cloned_repo.rs @@ -5,7 +5,7 @@ use std::time::Duration; use crate::pretty_print; use crate::types::cargo::manifest_path::{ManifestPath, MANIFEST_FILE_NAME}; use crate::types::cargo::metadata::CrateMetadata; -use crate::types::near::build::output::version_mismatch::VersionMismatch; +use crate::types::near::build::output::version_info::VersionInfo; use crate::types::near::build::side_effects::ArtifactMessages; use crate::types::near::docker_build::WARN_BECOMES_ERR; use crate::{camino, BuildArtifact}; @@ -67,7 +67,8 @@ impl ClonedRepo { path.push(MANIFEST_FILE_NAME); path }; - CrateMetadata::collect(ManifestPath::try_from(cargo_toml_path)?, no_locked).inspect_err(|err| { + let manifest_path = ManifestPath::try_from(cargo_toml_path)?; + CrateMetadata::collect(manifest_path, no_locked, None).inspect_err(|err| { if !no_locked && err.to_string().contains("Cargo.lock is absent") { no_locked_warn_pause(false); println!(); @@ -104,7 +105,8 @@ impl ClonedRepo { path.push(MANIFEST_FILE_NAME); path }; - CrateMetadata::collect(ManifestPath::try_from(cargo_toml_path)?, self.no_locked)? + let manifest_path = ManifestPath::try_from(cargo_toml_path)?; + CrateMetadata::collect(manifest_path, self.no_locked, None)? }; let destination_dir = @@ -149,7 +151,7 @@ fn copy( path: out_wasm_path, fresh: true, from_docker: true, - builder_version_mismatch: VersionMismatch::UnknownFromDocker, + builder_version_info: Some(VersionInfo::UnknownFromDocker), artifact_type: PhantomData, }; let mut messages = ArtifactMessages::default(); diff --git a/cargo-near-build/src/types/near/build/input/docker_context.rs b/cargo-near-build/src/types/near/docker_build/compute_command.rs similarity index 88% rename from cargo-near-build/src/types/near/build/input/docker_context.rs rename to cargo-near-build/src/types/near/docker_build/compute_command.rs index 4b20bff1..3318715c 100644 --- a/cargo-near-build/src/types/near/build/input/docker_context.rs +++ b/cargo-near-build/src/types/near/docker_build/compute_command.rs @@ -1,28 +1,19 @@ use colored::Colorize; -use crate::types::cargo::manifest_path::ManifestPath; +use super::metadata; impl super::Opts { - pub fn contract_path(&self) -> eyre::Result { - let contract_path: camino::Utf8PathBuf = if let Some(manifest_path) = &self.manifest_path { - let manifest_path = ManifestPath::try_from(manifest_path.clone())?; - manifest_path.directory()?.to_path_buf() - } else { - camino::Utf8PathBuf::from_path_buf(std::env::current_dir()?) - .map_err(|err| eyre::eyre!("Failed to convert path {}", err.to_string_lossy()))? - }; - Ok(contract_path) - } - const BUILD_COMMAND_CLI_CONFIG_ERR: &'static str = "flag cannot be used, when `container_build_command` is configured from `[package.metadata.near.reproducible_build]` in Cargo.toml"; pub fn get_cli_build_command_in_docker( &self, - manifest_command: Option>, - passed_env: Option>, + docker_build_meta: &metadata::ReproducibleBuild, ) -> eyre::Result> { - if let Some(manifest_command) = manifest_command { - self.append_env_suffix(manifest_command, passed_env) + if let Some(manifest_command) = docker_build_meta.container_build_command.as_ref() { + self.append_env_suffix( + manifest_command.clone(), + docker_build_meta.passed_env.clone(), + ) } else { println!( "{}", @@ -176,7 +167,7 @@ impl super::Opts { mod tests { #[test] fn test_passthrough_some_opts_into_docker_cmd() { - let opts = crate::BuildOpts { + let opts = crate::docker::DockerBuildOpts { no_release: true, no_abi: true, no_embed_abi: true, diff --git a/cargo-near-build/src/types/near/docker_build/metadata.rs b/cargo-near-build/src/types/near/docker_build/metadata.rs index 03b32740..d884f272 100644 --- a/cargo-near-build/src/types/near/docker_build/metadata.rs +++ b/cargo-near-build/src/types/near/docker_build/metadata.rs @@ -123,6 +123,15 @@ impl ReproducibleBuild { "`container_build_command`: `--no-locked` forbidden for `cargo near` build command", )); } + if is_cargo_near && command_token == "--manifest-path" { + return Err(eyre::eyre!( + "{}:\n{}\n{}", + "Malformed `[package.metadata.near.reproducible_build]` in Cargo.toml", + "`container_build_command`: `--manifest-path` isn't allowed to be specified \ + in manifest itself.", + "`--manifest-path ./Cargo.toml` is implied in all such cases", + )); + } } Ok(()) } diff --git a/cargo-near-build/src/types/near/docker_build/mod.rs b/cargo-near-build/src/types/near/docker_build/mod.rs index 7643106d..2261f819 100644 --- a/cargo-near-build/src/types/near/docker_build/mod.rs +++ b/cargo-near-build/src/types/near/docker_build/mod.rs @@ -1,4 +1,5 @@ -use crate::BuildOpts; +use crate::types::cargo::manifest_path::ManifestPath; +use crate::{CliDescription, ColorPreference}; use super::build::input::BuildContext; @@ -6,11 +7,51 @@ pub mod cloned_repo; pub mod crate_in_repo; pub mod metadata; +mod compute_command; pub mod subprocess; -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, bon::Builder)] pub struct Opts { - pub build_opts: BuildOpts, + /// disable implicit `--locked` flag for all `cargo` commands, enabled by default + #[builder(default)] + pub no_locked: bool, + /// Build contract in debug mode, without optimizations and bigger in size + #[builder(default)] + pub no_release: bool, + /// Do not generate ABI for the contract + #[builder(default)] + pub no_abi: bool, + /// Do not embed the ABI in the contract binary + #[builder(default)] + pub no_embed_abi: bool, + /// Do not include rustdocs in the embedded ABI + #[builder(default)] + pub no_doc: bool, + /// do not run `wasm-opt -O` on the generated output as a post-step + #[builder(default)] + pub no_wasmopt: bool, + /// Copy final artifacts to this directory + pub out_dir: Option, + /// Path to the `Cargo.toml` of the contract to build + pub manifest_path: Option, + /// Set compile-time feature flags. + #[builder(into)] + pub features: Option, + /// Disables default feature flags. + #[builder(default)] + pub no_default_features: bool, + /// Coloring: auto, always, never; + /// assumed to be auto when `None` + pub color: Option, + /// description of cli command, where [BuildOpts](crate::BuildOpts) are being used from, either real + /// or emulated + #[builder(default)] + pub cli_description: CliDescription, + /// additional environment key-value pairs, that should be passed to underlying + /// build commands + #[builder(default)] + pub env: Vec<(String, String)>, + #[builder(default)] pub context: BuildContext, } @@ -22,3 +63,16 @@ impl Default for BuildContext { pub const WARN_BECOMES_ERR: &str = "This WARNING becomes a hard ERROR when deploying contract with docker."; + +impl Opts { + pub fn contract_path(&self) -> eyre::Result { + let contract_path: camino::Utf8PathBuf = if let Some(manifest_path) = &self.manifest_path { + let manifest_path = ManifestPath::try_from(manifest_path.clone())?; + manifest_path.directory()?.to_path_buf() + } else { + camino::Utf8PathBuf::from_path_buf(std::env::current_dir()?) + .map_err(|err| eyre::eyre!("Failed to convert path {}", err.to_string_lossy()))? + }; + Ok(contract_path) + } +} diff --git a/cargo-near/src/commands/build_command/mod.rs b/cargo-near/src/commands/build_command/mod.rs index 559e87c7..d2fbd5d5 100644 --- a/cargo-near/src/commands/build_command/mod.rs +++ b/cargo-near/src/commands/build_command/mod.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use cargo_near_build::docker; use cargo_near_build::{env_keys, BuildArtifact, BuildContext, BuildOpts}; #[derive(Debug, Default, Clone, interactive_clap::InteractiveClap)] @@ -76,10 +77,8 @@ impl BuildCommand { } cargo_near_build::build(self.into()) } else { - cargo_near_build::docker::build(cargo_near_build::docker::DockerBuildOpts { - build_opts: self.into(), - context, - }) + let docker_opts = docker_opts_from((self, context)); + cargo_near_build::docker::build(docker_opts) } } pub fn no_docker(&self) -> bool { @@ -137,9 +136,34 @@ impl From for BuildOpts { color: value.color.map(Into::into), cli_description: Default::default(), env: get_env_key_vals(value.env), + override_nep330_contract_path: None, + override_cargo_target_dir: None, } } } + +/// this is more or less equivalent to +/// impl From<(BuildCommand, BuildContext)> for docker::DockerBuildOpts +/// which is not possible due to BuildContext being a non-local type to current (cli) crate +fn docker_opts_from(value: (BuildCommand, BuildContext)) -> docker::DockerBuildOpts { + docker::DockerBuildOpts { + no_locked: value.0.no_locked, + no_release: value.0.no_release, + no_abi: value.0.no_abi, + no_embed_abi: value.0.no_embed_abi, + no_doc: value.0.no_doc, + no_wasmopt: value.0.no_wasmopt, + features: value.0.features, + no_default_features: value.0.no_default_features, + out_dir: value.0.out_dir.map(Into::into), + manifest_path: value.0.manifest_path.map(Into::into), + color: value.0.color.map(Into::into), + cli_description: Default::default(), + env: get_env_key_vals(value.0.env), + context: value.1, + } +} + #[derive(Debug, Clone)] pub struct BuildCommandlContext; diff --git a/cargo-near/src/commands/mod.rs b/cargo-near/src/commands/mod.rs index 039bf2a1..37788456 100644 --- a/cargo-near/src/commands/mod.rs +++ b/cargo-near/src/commands/mod.rs @@ -33,7 +33,7 @@ pub enum NearCommand { To create an account on a different network, use NEAR CLI [https://near.cli.rs]" ))] /// Create a development account using the faucet service sponsor to receive some NEAR tokens (testnet only) - /// To create an account on a different network, use NEAR CLI [https://near.cli.rs] + /// To create an account on a different network, use NEAR CLI CreateDevAccount(self::create_dev_account::CreateAccount), #[strum_discriminants(strum(message = "deploy - Add a new contract code"))] /// Add a new contract code diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index e2a0bdbb..52f7a78e 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -7,6 +7,10 @@ publish = false [dependencies] const_format = "0.2" cargo-near-build = { version = "0.2.0", path = "../cargo-near-build" } +cargo-near = { path = "../cargo-near" } +colored = "2.0" +tracing = "0.1.40" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } [dev-dependencies] borsh = { version = "1.0.0", features = ["derive", "unstable__schema"] } @@ -26,5 +30,3 @@ tokio = "1.0" quote = "1.0" near-workspaces = "0.14.0" zstd = "0.13" -env_logger = "0.11.5" -log = "0.4" diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index 5beb855d..2c46258a 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -1,3 +1,5 @@ +use cargo_near_build::camino; + /// NOTE: `near-sdk` version, published on crates.io pub mod from_crates_io { use const_format::formatcp; @@ -6,6 +8,29 @@ pub mod from_crates_io { pub const SDK_VERSION_TOML: &str = formatcp!(r#"version = "{SDK_VERSION}""#); } +pub fn setup_tracing() { + use colored::Colorize; + use tracing_subscriber::layer::SubscriberExt; + use tracing_subscriber::util::SubscriberInitExt; + use tracing_subscriber::EnvFilter; + use tracing_subscriber::{fmt::format, prelude::*}; + let environment = "test".green(); + let my_formatter = cargo_near::types::my_formatter::MyFormatter::from_environment(environment); + + let format = format::debug_fn(move |writer, _field, value| write!(writer, "{:?}", value)); + + let env_filter = EnvFilter::from_default_env(); + + let _e = tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .event_format(my_formatter) + .fmt_fields(format) + .with_filter(env_filter), + ) + .try_init(); +} + /// NOTE: this version is version of near-sdk in arbitrary revision from N.x.x development cycle pub mod from_git { use const_format::formatcp; @@ -25,11 +50,20 @@ pub mod from_git { ); } +pub fn common_root_for_test_projects_build() -> camino::Utf8PathBuf { + let manifest_dir: camino::Utf8PathBuf = env!("CARGO_MANIFEST_DIR").into(); + let workspace_dir = manifest_dir + .parent() + .unwrap() + .join("target") + .join("_abi-integration-tests"); + workspace_dir +} + #[macro_export] macro_rules! invoke_cargo_near { ($(Cargo: $cargo_path:expr;)? $(Vars: $cargo_vars:expr;)? Opts: $cli_opts:expr; Code: $($code:tt)*) => {{ - let manifest_dir: camino::Utf8PathBuf = env!("CARGO_MANIFEST_DIR").into(); - let workspace_dir = manifest_dir.parent().unwrap().join("target").join("_abi-integration-tests"); + let workspace_dir = $crate::common_root_for_test_projects_build(); let crate_dir = workspace_dir.join(function_name!()); let src_dir = crate_dir.join("src"); std::fs::create_dir_all(&src_dir)?; @@ -56,11 +90,9 @@ macro_rules! invoke_cargo_near { let lib_rs_path = src_dir.join("lib.rs"); std::fs::write(lib_rs_path, lib_rs)?; - std::env::set_var("CARGO_TARGET_DIR", workspace_dir.join("target")); - let cargo_near::CliOpts::Near(cli_args) = cargo_near::Opts::try_parse_from($cli_opts.split(" "))?; - match cli_args.cmd { + let path: camino::Utf8PathBuf = match cli_args.cmd { Some(cargo_near::commands::CliNearCommand::Abi(cmd)) => { let args = cargo_near_build::abi::AbiOpts { no_locked: cmd.no_locked, @@ -70,7 +102,9 @@ macro_rules! invoke_cargo_near { manifest_path: Some(cargo_path.into()), color: cmd.color.map(Into::into), }; - cargo_near_build::abi::build(args)?; + tracing::debug!("AbiOpts: {:#?}", args); + let path = cargo_near_build::abi::build(args)?; + path }, Some(cargo_near::commands::CliNearCommand::Build(cmd)) => { let args = { @@ -78,13 +112,15 @@ macro_rules! invoke_cargo_near { args.manifest_path = Some(cargo_path.into()); args }; - args.run(cargo_near_build::BuildContext::Build)?; + tracing::debug!("BuildCommand: {:#?}", args); + let artifact = args.run(cargo_near_build::BuildContext::Build)?; + artifact.path }, Some(_) => todo!(), - None => () - } + None => unreachable!(), + }; + path - workspace_dir.join("target").join("near") }}; } @@ -93,12 +129,13 @@ macro_rules! generate_abi_with { ($(Cargo: $cargo_path:expr;)? $(Vars: $cargo_vars:expr;)? $(Opts: $cli_opts:expr;)? Code: $($code:tt)*) => {{ let opts = "cargo near abi --no-locked"; $(let opts = format!("cargo near abi --no-locked {}", $cli_opts);)?; - let result_dir = $crate::invoke_cargo_near! { + let result_file = $crate::invoke_cargo_near! { $(Cargo: $cargo_path;)? $(Vars: $cargo_vars;)? Opts: &opts; Code: $($code)* }; + let result_dir = result_file.as_std_path().parent().expect("has parent"); let abi_root: cargo_near_build::near_abi::AbiRoot = serde_json::from_slice(&std::fs::read(result_dir.join(format!("{}_abi.json", function_name!())))?)?; @@ -162,28 +199,17 @@ macro_rules! build_with { ($(Cargo: $cargo_path:expr;)? $(Vars: $cargo_vars:expr;)? $(Opts: $cli_opts:expr;)? Code: $($code:tt)*) => {{ let opts = "cargo near build --no-docker --no-locked"; $(let opts = format!("cargo near build --no-docker --no-locked {}", $cli_opts);)?; - let result_dir = $crate::invoke_cargo_near! { + let result_file = $crate::invoke_cargo_near! { $(Cargo: $cargo_path;)? $(Vars: $cargo_vars;)? Opts: &opts; Code: $($code)* }; + let result_dir = result_file.as_std_path().parent().expect("has parent"); - let manifest_dir: std::path::PathBuf = env!("CARGO_MANIFEST_DIR").into(); - let workspace_dir = manifest_dir.parent().unwrap().join("target").join("_abi-integration-tests"); - let wasm_debug_path = workspace_dir.join("target") - .join("wasm32-unknown-unknown") - .join("debug") - .join(format!("{}.wasm", function_name!())); - let wasm_release_path = workspace_dir.join("target") - .join("wasm32-unknown-unknown") - .join("release") - .join(format!("{}.wasm", function_name!())); - let wasm: Vec = if wasm_release_path.exists() { - std::fs::read(wasm_release_path)? - } else { - std::fs::read(wasm_debug_path)? - }; + let wasm_path = result_dir. + join(format!("{}.wasm", function_name!())); + let wasm: Vec = std::fs::read(wasm_path)?; let abi_path = result_dir.join(format!("{}_abi.json", function_name!())); let abi_root: Option = if abi_path.exists() { diff --git a/integration-tests/tests/abi/borsh_schema.rs b/integration-tests/tests/abi/borsh_schema.rs index 5c8522da..b0f5140c 100644 --- a/integration-tests/tests/abi/borsh_schema.rs +++ b/integration-tests/tests/abi/borsh_schema.rs @@ -1,13 +1,14 @@ use crate::util::AsBorshSchema; use borsh::schema::{BorshSchemaContainer, Definition, Fields}; -use cargo_near_integration_tests::{generate_abi, generate_abi_fn}; +use cargo_near_integration_tests::{generate_abi, generate_abi_fn, setup_tracing}; use function_name::named; use std::collections::BTreeMap; #[test] #[named] fn test_borsh_schema_numeric_primitives_signed() -> cargo_near::CliResult { - let _e = env_logger::Builder::from_default_env().try_init(); + setup_tracing(); + let abi_root = generate_abi_fn! { pub fn foo( &self, diff --git a/integration-tests/tests/abi/opts.rs b/integration-tests/tests/abi/opts.rs index 18408002..4399b76f 100644 --- a/integration-tests/tests/abi/opts.rs +++ b/integration-tests/tests/abi/opts.rs @@ -1,10 +1,13 @@ -use cargo_near_integration_tests::generate_abi_fn_with; +use cargo_near_integration_tests::{ + common_root_for_test_projects_build, generate_abi_fn_with, setup_tracing, +}; use function_name::named; use std::fs; #[test] #[named] fn test_abi_no_doc() -> cargo_near::CliResult { + setup_tracing(); let abi_root = generate_abi_fn_with! { Opts: "--no-doc"; Code: @@ -24,6 +27,7 @@ fn test_abi_no_doc() -> cargo_near::CliResult { #[test] #[named] fn test_abi_opt_compact_abi() -> cargo_near::CliResult { + setup_tracing(); generate_abi_fn_with! { Opts: "--compact-abi"; Code: @@ -32,18 +36,16 @@ fn test_abi_opt_compact_abi() -> cargo_near::CliResult { } }; - let manifest_dir: std::path::PathBuf = env!("CARGO_MANIFEST_DIR").into(); - let workspace_dir = manifest_dir - .parent() - .unwrap() + let workspace_dir = common_root_for_test_projects_build(); + let expected_target_dir = workspace_dir + .join(function_name!()) .join("target") - .join("_abi-integration-tests"); - let abi_json = fs::read_to_string( - workspace_dir - .join("target") - .join("near") - .join(format!("{}_abi.json", function_name!())), - )?; + .join("near"); + + tracing::info!("expected target dir: {:?}", expected_target_dir); + + let abi_json = + fs::read_to_string(expected_target_dir.join(format!("{}_abi.json", function_name!())))?; assert_eq!(minifier::json::minify(&abi_json).to_string(), abi_json); @@ -53,6 +55,7 @@ fn test_abi_opt_compact_abi() -> cargo_near::CliResult { #[test] #[named] fn test_abi_opt_out_dir() -> cargo_near::CliResult { + setup_tracing(); let out_dir = tempfile::tempdir()?; let abi_root = generate_abi_fn_with! { Opts: format!("--out-dir {}", out_dir.path().display()); diff --git a/integration-tests/tests/build/opts.rs b/integration-tests/tests/build/opts.rs index 8b3d93ea..6db82b3d 100644 --- a/integration-tests/tests/build/opts.rs +++ b/integration-tests/tests/build/opts.rs @@ -1,5 +1,5 @@ use crate::build::util; -use cargo_near_integration_tests::build_fn_with; +use cargo_near_integration_tests::{build_fn_with, setup_tracing}; use function_name::named; use std::fs; @@ -86,6 +86,7 @@ fn test_build_opt_out_dir() -> cargo_near::CliResult { #[tokio::test] #[named] async fn test_build_no_release() -> cargo_near::CliResult { + setup_tracing(); let build_result = build_fn_with! { Opts: "--no-release"; Code: diff --git a/integration-tests/tests/docker.rs b/integration-tests/tests/docker.rs index d6d1a5f4..575f5963 100644 --- a/integration-tests/tests/docker.rs +++ b/integration-tests/tests/docker.rs @@ -1,7 +1,9 @@ #[cfg(target_os = "linux")] #[test] fn test_docker_build() -> cargo_near::CliResult { - let _e = env_logger::Builder::from_default_env().try_init(); + use cargo_near_integration_tests::setup_tracing; + + setup_tracing(); let manifest_dir: camino::Utf8PathBuf = env!("CARGO_MANIFEST_DIR").into(); let cargo_near::CliOpts::Near(cli_args) =