Skip to content

Commit

Permalink
refactor: Use cargo-near to build project (#275)
Browse files Browse the repository at this point in the history
Co-authored-by: Vlad Frolov <frolvlad@gmail.com>
  • Loading branch information
ghimire007 and frol committed Jun 26, 2023
1 parent 26ca5ae commit c31a955
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 117 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
/target
Cargo.lock

#include all target folders
target
# Code Editor related files
.idea
.vscode
Expand Down
1 change: 1 addition & 0 deletions workspaces/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
144 changes: 28 additions & 116 deletions workspaces/src/cargo/mod.rs
Original file line number Diff line number Diff line change
@@ -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<Metadata> {
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<P: AsRef<Path> + Debug>(project_path: P) -> Result<Vec<Message>> {
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<Vec<u8>> {
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<Vec<u8>> {
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::<Vec<_>>();
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))
}

0 comments on commit c31a955

Please sign in to comment.