diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 572e3139..a41ad480 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,7 +31,10 @@ jobs: strategy: matrix: platform: [ubuntu-latest, macos-latest] - toolchain: [stable, 1.68.0] + # TODO: Bring `stable` back to the matrix once Rust 1.70.0+ compatibility is resolved in nearcore: + # https://github.com/near/nearcore/issues/9143 + #toolchain: [stable, 1.68.0] + toolchain: [1.68.0] runs-on: ${{ matrix.platform }} steps: @@ -41,6 +44,7 @@ jobs: with: profile: minimal toolchain: ${{ matrix.toolchain }} + default: true - uses: Swatinem/rust-cache@v1 - name: Add wasm32 target run: rustup target add wasm32-unknown-unknown diff --git a/.gitignore b/.gitignore index a3d0e5cb..3a52c10b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /target Cargo.lock +#include all target folders +target # Code Editor related files .idea .vscode diff --git a/workspaces/Cargo.toml b/workspaces/Cargo.toml index 4351032b..9dfee876 100644 --- a/workspaces/Cargo.toml +++ b/workspaces/Cargo.toml @@ -16,6 +16,7 @@ base64 = "0.13" borsh = "0.10" bs58 = "0.4" cargo_metadata = { version = "0.15.4", optional = true } +cargo-near = "0.3.1" chrono = "0.4.19" fs2 = "0.4" hex = "0.4.2" diff --git a/workspaces/src/cargo/mod.rs b/workspaces/src/cargo/mod.rs index 7067b38f..6900eecd 100644 --- a/workspaces/src/cargo/mod.rs +++ b/workspaces/src/cargo/mod.rs @@ -1,129 +1,41 @@ -use async_process::Command; -use cargo_metadata::{Error as MetadataError, Message, Metadata, MetadataCommand}; -use tracing::debug; - -use std::env; -use std::fmt::Debug; -use std::fs; -use std::io; -use std::path::Path; -use std::process::Stdio; +use std::convert::TryInto; use crate::error::ErrorKind; -use crate::Result; - -fn cargo_bin() -> Command { - match env::var_os("CARGO") { - Some(cargo) => Command::new(cargo), - None => Command::new("cargo"), - } -} - -/// Fetch current project's metadata (i.e. project invoking this method, not the one that we are -/// trying to compile). -fn root_cargo_metadata() -> Result { - MetadataCommand::new().exec().map_err(|e| match e { - // comes from cargo metadata command error message, so IO should be appropriate - MetadataError::CargoMetadata { stderr } => ErrorKind::Io.message(stderr), - MetadataError::Io(err) => ErrorKind::Io.custom(err), - MetadataError::Utf8(err) => ErrorKind::DataConversion.custom(err), - MetadataError::ErrUtf8(err) => ErrorKind::DataConversion.custom(err), - MetadataError::Json(err) => ErrorKind::DataConversion.custom(err), - err @ MetadataError::NoJson => ErrorKind::DataConversion.message(err.to_string()), - }) -} - -async fn build_cargo_project + Debug>(project_path: P) -> Result> { - let metadata = root_cargo_metadata()?; - let output = cargo_bin() - .args([ - "build", - "--target", - "wasm32-unknown-unknown", - "--release", - "--message-format=json", - "--target-dir", - metadata.target_directory.as_str(), - ]) - .current_dir(&project_path) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output() - .await - .map_err(|e| ErrorKind::Io.custom(e))?; - - debug!( - target: "workspaces", - "Building project '{:?}' resulted in status {:?}", - &project_path, output.status - ); - - if output.status.success() { - let reader = std::io::BufReader::new(output.stdout.as_slice()); - Ok(Message::parse_stream(reader).map(|m| m.unwrap()).collect()) - } else { - Err(ErrorKind::Io.message(format!( - "Failed to build project '{:?}'.\n\ - Stderr:\n\ - {}\n\ - Stdout:\n\ - {}", - project_path, - String::from_utf8(output.stderr).map_err(|e| ErrorKind::DataConversion.custom(e))?, - String::from_utf8(output.stdout).map_err(|e| ErrorKind::DataConversion.custom(e))?, - ))) - } -} /// Builds the cargo project located at `project_path` and returns the generated wasm file contents. /// /// NOTE: This function does not check whether the resulting wasm file is a valid smart /// contract or not. -pub async fn compile_project(project_path: &str) -> Result> { - let project_path = fs::canonicalize(project_path).map_err(|e| match e.kind() { - io::ErrorKind::NotFound => ErrorKind::Io.message(format!( +pub async fn compile_project(project_path: &str) -> crate::Result> { + let project_path = std::fs::canonicalize(project_path).map_err(|e| match e.kind() { + std::io::ErrorKind::NotFound => ErrorKind::Io.message(format!( "Incorrect file supplied to compile_project('{}')", project_path )), _ => ErrorKind::Io.custom(e), })?; - let messages = build_cargo_project(project_path).await?; - - // We find the last compiler artifact message which should contain information about the - // resulting .wasm file - let compile_artifact = messages - .iter() - .filter_map(|m| match m { - cargo_metadata::Message::CompilerArtifact(artifact) => Some(artifact), - _ => None, - }) - .last() - .ok_or_else(|| { - ErrorKind::Io.message( - "Cargo failed to produce any compilation artifacts. \ - Please check that your project contains a NEAR smart contract.", - ) - })?; - // The project could have generated many auxiliary files, we are only interested in .wasm files - let wasm_files = compile_artifact - .filenames - .iter() - .filter(|f| f.as_str().ends_with(".wasm")) - .collect::>(); - match wasm_files.as_slice() { - [] => Err(ErrorKind::Io.message( - "Compilation resulted in no '.wasm' target files. \ - Please check that your project contains a NEAR smart contract.", - )), - [file] => { - let file = file.canonicalize().map_err(|e| ErrorKind::Io.custom(e))?; - Ok(tokio::fs::read(file) - .await - .map_err(|e| ErrorKind::Io.custom(e))?) - } - _ => Err(ErrorKind::Io.message(format!( - "Compilation resulted in more than one '.wasm' target file: {:?}", - wasm_files - ))), - } + let cargo_near_build_command = cargo_near::BuildCommand { + release: true, + embed_abi: true, + doc: false, + color: cargo_near::ColorPreference::Always, + no_abi: true, + out_dir: None, + manifest_path: Some( + project_path + .join("Cargo.toml") + .try_into() + .map_err(|e| ErrorKind::Io.custom(e))?, + ), + }; + let compile_artifact = + cargo_near::build::run(cargo_near_build_command).map_err(|e| ErrorKind::Io.custom(e))?; + + let file = compile_artifact + .path + .canonicalize() + .map_err(|e| ErrorKind::Io.custom(e))?; + tokio::fs::read(file) + .await + .map_err(|e| ErrorKind::Io.custom(e)) }