Skip to content

Commit

Permalink
fix: Build contracts with library feature-flags separately (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
mandrean authored Jul 22, 2022
1 parent 55f2572 commit a11c753
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 30 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "tests/cw-plus"]
path = tests/cw-plus
url = https://github.com/CosmWasm/cw-plus.git
27 changes: 15 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ path = "src/main.rs"
anyhow = "1"
binaryen = "0.12"
cargo = "0.63"
cargo-util = "0.2"
clap = { version = "3", features = ["derive"] }
glob = "0.3"
hex = "0.4"
itertools = "0.10"
lazy_static = "1.4"
num_cpus = "1.13"
path-absolutize = "3.0"
Expand Down
28 changes: 24 additions & 4 deletions src/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use cargo::{
core::{
compiler::{BuildConfig, CompileKind, CompileMode, CompileTarget, MessageFormat},
resolver::CliFeatures,
Workspace,
Package, Workspace,
},
ops,
ops::{CompileFilter, CompileOptions},
Expand All @@ -21,9 +21,9 @@ lazy_static! {
CompileKind::Target(CompileTarget::new(TARGET_WASM32).expect("couldn't create target"));
}

/// Compiles the workspace and returns the created WASM artifacts.
pub fn compile(compile_opts: &CompileOptions, ws: &Workspace) -> Result<Vec<PathBuf>> {
let wasm_paths = ops::compile(ws, compile_opts)?
/// Compiles the workspace packages and returns the paths to the created WASM artifacts.
pub fn compile(cfg: &Config, ws: &Workspace, packages: ops::Packages) -> Result<Vec<PathBuf>> {
let wasm_paths = ops::compile(ws, &compile_opts(cfg, packages)?)?
.cdylibs
.into_iter()
.filter(|o| o.unit.kind.eq(&KIND_WASM32))
Expand All @@ -33,6 +33,26 @@ pub fn compile(compile_opts: &CompileOptions, ws: &Workspace) -> Result<Vec<Path
Ok(wasm_paths)
}

/// Variant of [`compile()`](fn@compile) which compiles each package individually in using ephemeral workspaces.
pub fn compile_ephemerally(
cfg: &Config,
packages: Vec<Package>,
) -> anyhow::Result<Vec<PathBuf>> {
packages
.into_iter()
.map(|p| {
(
p.package_id().name().to_string(),
Workspace::ephemeral(p, cfg, None, false),
)
})
.try_fold(vec![], |mut acc, (package, ws)| {
let mut res = compile(cfg, &ws?, ops::Packages::Packages(vec![package]))?;
acc.append(&mut res);
anyhow::Ok(acc)
})
}

/// Sets up the high-level compilation options.
pub fn compile_opts(config: &Config, spec: ops::Packages) -> Result<CompileOptions> {
Ok(CompileOptions {
Expand Down
84 changes: 78 additions & 6 deletions src/ext.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
use std::path::PathBuf;

pub trait RTake<R> {
/// Returns the last n items of a slice.
pub trait TakeExt<R> {
/// Returns the first n items in `R`.
fn ltake(&self, n: usize) -> R;

/// Returns the last n items in `R`.
fn rtake(&self, n: usize) -> R;

/// Skips the first n items in `R`.
fn lskip(&self, n: usize) -> R;

/// Skips the last n items in `R`.
fn rskip(&self, n: usize) -> R;
}

impl<T> RTake<Vec<T>> for [T]
impl<T> TakeExt<Vec<T>> for [T]
where
T: Clone,
{
fn ltake(&self, n: usize) -> Vec<T> {
self[0..n].to_vec()
}

fn rtake(&self, n: usize) -> Vec<T> {
(&self[self.len() - n..]).to_vec()
self[self.len() - n..].to_vec()
}

fn lskip(&self, n: usize) -> Vec<T> {
self[n..].to_vec()
}

fn rskip(&self, n: usize) -> Vec<T> {
self[0..self.len() - n].to_vec()
}
}

impl RTake<PathBuf> for PathBuf {
impl TakeExt<PathBuf> for PathBuf {
fn ltake(&self, n: usize) -> PathBuf {
self.iter()
.map(PathBuf::from)
.collect::<Vec<PathBuf>>()
.ltake(n)
.iter()
.fold(PathBuf::new(), |acc, i| acc.join(i))
}

fn rtake(&self, n: usize) -> PathBuf {
self.iter()
.map(PathBuf::from)
Expand All @@ -23,11 +53,37 @@ impl RTake<PathBuf> for PathBuf {
.iter()
.fold(PathBuf::new(), |acc, i| acc.join(i))
}

fn lskip(&self, n: usize) -> PathBuf {
self.iter()
.map(PathBuf::from)
.collect::<Vec<PathBuf>>()
.lskip(n)
.iter()
.fold(PathBuf::new(), |acc, i| acc.join(i))
}

fn rskip(&self, n: usize) -> PathBuf {
self.iter()
.map(PathBuf::from)
.collect::<Vec<PathBuf>>()
.rskip(n)
.iter()
.fold(PathBuf::new(), |acc, i| acc.join(i))
}
}

#[cfg(test)]
mod tests {
use crate::ext::RTake;
use crate::ext::TakeExt;

#[test]
fn returns_first_n_items() {
assert_eq!(
vec!["May", "I", "Speak"],
vec!["May", "I", "Speak", "To", "The", "Manager"].ltake(3)
);
}

#[test]
fn returns_last_n_items() {
Expand All @@ -36,4 +92,20 @@ mod tests {
vec!["May", "I", "Speak", "To", "The", "Manager"].rtake(2)
);
}

#[test]
fn skips_first_n_items() {
assert_eq!(
vec!["The", "Manager"],
vec!["May", "I", "Speak", "To", "The", "Manager"].lskip(4)
);
}

#[test]
fn skips_last_n_items() {
assert_eq!(
vec!["May", "I", "Speak"],
vec!["May", "I", "Speak", "To", "The", "Manager"].rskip(3)
);
}
}
2 changes: 1 addition & 1 deletion src/hashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use anyhow::{anyhow, Result};
use hex::ToHex;
use ring::digest::{Context, Digest, SHA256};

use crate::ext::RTake;
use crate::ext::TakeExt;

/// Calculates the SHA-256 digest of a buffer.
pub fn sha256_digest<R: Read>(mut reader: R) -> Result<Digest> {
Expand Down
41 changes: 35 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{fs, fs::File, io::Read, path::PathBuf};

use anyhow::Error;
use cargo::{core::Workspace, ops};
use cargo::{core::Workspace, ops, util::interning::InternedString};
use path_absolutize::Absolutize;

use crate::{compilation::*, ext::*, hashing::*, optimization::*};
Expand All @@ -11,21 +11,50 @@ pub mod ext;
pub mod hashing;
pub mod optimization;

/// Runs cw-optimizoor against the manifest path.
pub fn run(manifest_path: &PathBuf) -> anyhow::Result<(), Error> {
let cfg = config()?;
let ws = Workspace::new(manifest_path.as_ref(), &cfg).expect("couldn't create workspace");
let contracts = ws
let output_dir = create_artifacts_dir(&ws)?;

// all ws members that are contracts
let all_contracts = ws
.members()
.filter(|&p| p.manifest_path().starts_with(&ws.root().join("contracts")))
.collect::<Vec<_>>();

// collect ws members with deps with feature = library to be compiled individually
let individual_contracts = all_contracts
.iter()
.filter(|p| {
p.dependencies()
.iter()
.any(|d| d.features().contains(&InternedString::from("library")))
})
.map(|&p| p.clone())
.collect::<Vec<_>>();

// package names of contracts to be compiled individually
let individual_names = individual_contracts
.iter()
.map(|p| p.package_id().name().to_string())
.collect::<Vec<String>>();
let compile_opts = compile_opts(&cfg, ops::Packages::Packages(contracts))?;
let output_dir = create_artifacts_dir(&ws)?;
.collect::<Vec<_>>();

// package names of contracts to be compiled together
let common_names = all_contracts
.iter()
.map(|p| p.package_id().name().to_string())
.filter(|name| !individual_names.contains(name))
.collect::<Vec<_>>();

println!("🧐️ Compiling .../{}", &manifest_path.rtake(2).display());
let intermediate_wasm_paths = compile(&compile_opts, &ws)?;
let mut intermediate_wasm_paths = compile(&cfg, &ws, ops::Packages::Packages(common_names))?;
let mut special_intermediate_wasm_paths = compile_ephemerally(&cfg, individual_contracts)?;
intermediate_wasm_paths.append(&mut special_intermediate_wasm_paths);

println!("🤓 Intermediate checksums:");
let mut prev_intermediate_checksums = String::new();

File::options()
.read(true)
.write(true)
Expand Down
1 change: 0 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use anyhow::Result;

use clap::Parser;
use path_absolutize::Absolutize;

Expand Down
17 changes: 17 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Integration Tests

The integration tests run against [CosmWasm/cw-plus] and use [wabt] to verify the outputs.

### Setup
```sh
$ brew install wabt
$ git submodule update --init
```

### Run
```sh
$ cargo run test
```

[CosmWasm/cw-plus]: https://github.com/CosmWasm/cw-plus
[wabt]: https://github.com/WebAssembly/wabt
1 change: 1 addition & 0 deletions tests/cw-plus
Submodule cw-plus added at a24bed
35 changes: 35 additions & 0 deletions tests/cw_plus.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use glob::glob;
use std::env;
use std::process::{Command, Stdio};

#[test]
fn it_can_compile_repo() {
let cwd = env::current_dir().unwrap();
let manifest_path = &cwd.join("tests/cw-plus/Cargo.toml");
let res = cw_optimizoor::run(manifest_path);

let wasm_pattern = &*cwd
.join("tests/cw-plus/artifacts/*.wasm")
.to_str()
.unwrap_or_default()
.to_string();
for entry in glob(wasm_pattern).expect("Failed to read glob pattern") {
let path = format!("{}", entry.unwrap().display());
println!("{}", path);
let decomp = Command::new("wasm-decompile")
.arg(path)
.stdout(Stdio::piped())
.spawn()
.unwrap();

let grep = Command::new("grep")
.arg("function execute")
.stdin(decomp.stdout.unwrap())
.output()
.unwrap();

assert!(grep.status.success())
}

assert!(res.is_ok());
}

0 comments on commit a11c753

Please sign in to comment.