From 39c1ffe4b039dfd94f98131a5f29f623e51a9f3a Mon Sep 17 00:00:00 2001 From: Ilyar <761285+ilyar@users.noreply.github.com> Date: Fri, 14 Oct 2022 04:55:08 +0200 Subject: [PATCH 01/13] fix fmt --- sold/build.rs | 23 +++++++-- sold/src/lib.rs | 116 ++++++++++++++++++++++++++------------------ sold/src/main.rs | 2 +- sold/src/printer.rs | 28 ++++++++--- sold/tests/tests.rs | 18 ++++--- 5 files changed, 122 insertions(+), 65 deletions(-) diff --git a/sold/build.rs b/sold/build.rs index f349a23..9f77025 100644 --- a/sold/build.rs +++ b/sold/build.rs @@ -19,19 +19,34 @@ fn main() { for lib in ["solc", "solidity", "langutil", "solutil"] { if cfg!(target_os = "windows") { - println!("cargo:rustc-link-search=native={}/build/lib{}/{}", sol2tvm.display(), lib, profile); + println!( + "cargo:rustc-link-search=native={}/build/lib{}/{}", + sol2tvm.display(), + lib, + profile + ); } else { - println!("cargo:rustc-link-search=native={}/build/lib{}", sol2tvm.display(), lib); + println!( + "cargo:rustc-link-search=native={}/build/lib{}", + sol2tvm.display(), + lib + ); } println!("cargo:rustc-link-lib=static={}", lib); } println!("cargo:rustc-link-search=native={}/lib", sol2tvm.display()); - println!("cargo:rustc-link-search=native={}/build/deps/lib", sol2tvm.display()); + println!( + "cargo:rustc-link-search=native={}/build/deps/lib", + sol2tvm.display() + ); if cfg!(target_os = "windows") { let path = std::path::PathBuf::from("../compiler/deps/install/win64/lib"); - println!("cargo:rustc-link-search=native={}", std::fs::canonicalize(path).unwrap().display()); + println!( + "cargo:rustc-link-search=native={}", + std::fs::canonicalize(path).unwrap().display() + ); } else if cfg!(target_os = "macos") { println!("cargo:rustc-link-search=native=/opt/homebrew/lib"); println!("cargo:rustc-link-search=native=/usr/local/lib"); diff --git a/sold/src/lib.rs b/sold/src/lib.rs index dbe2761..3918641 100644 --- a/sold/src/lib.rs +++ b/sold/src/lib.rs @@ -29,7 +29,7 @@ unsafe extern "C" fn read_callback( .into_owned(); if kind != "source" { *o_error = make_error(format!("Unknown kind \"{}\"", kind)); - return + return; } let filename = std::ffi::CStr::from_ptr(data) .to_string_lossy() @@ -38,7 +38,7 @@ unsafe extern "C" fn read_callback( Ok(f) => f, Err(e) => { *o_error = make_error(format!("Failed to open file: {}", e)); - return + return; } }; let mut buf = vec![]; @@ -63,8 +63,11 @@ pub fn solidity_version() -> String { } fn compile(args: &Args, input: &str) -> Result { - let include_paths = args.include_path.iter() - .map(|x| format!("\"{}\"", x)).collect::>() + let include_paths = args + .include_path + .iter() + .map(|x| format!("\"{}\"", x)) + .collect::>() .join(", "); let show_function_ids = if args.function_ids { ", \"showFunctionIds\"" @@ -84,7 +87,8 @@ fn compile(args: &Args, input: &str) -> Result { let force_remote_update = args.tvm_refresh_remote; let main_contract = args.contract.clone().unwrap_or_default(); let input = serde_json::ser::to_string(input)?; - let input = format!(r#" + let input = format!( + r#" {{ "language": "Solidity", "settings": {{ @@ -104,7 +108,8 @@ fn compile(args: &Args, input: &str) -> Result { }} }} }} - "#); + "# + ); let input_cstring = std::ffi::CString::new(input).expect("Failed to create CString"); let output = unsafe { std::ffi::CStr::from_ptr(libsolc::solidity_compile( @@ -112,8 +117,8 @@ fn compile(args: &Args, input: &str) -> Result { Some(read_callback), std::ptr::null_mut(), )) - .to_string_lossy() - .into_owned() + .to_string_lossy() + .into_owned() }; let res = serde_json::from_str(output.as_str())?; Ok(res) @@ -134,20 +139,20 @@ fn parse_comp_result( let res = res.as_object().ok_or_else(|| parse_error!())?; if let Some(v) = res.get("errors") { - let entries = v.as_array() - .ok_or_else(|| parse_error!())?; + let entries = v.as_array().ok_or_else(|| parse_error!())?; let mut severe = false; for entry in entries { - let entry = entry.as_object() - .ok_or_else(|| parse_error!())?; - let severity = entry.get("severity") + let entry = entry.as_object().ok_or_else(|| parse_error!())?; + let severity = entry + .get("severity") .ok_or_else(|| parse_error!())? .as_str() .ok_or_else(|| parse_error!())?; if severity == "error" { severe = true; } - let message = entry.get("humanFormattedMessage") + let message = entry + .get("humanFormattedMessage") .ok_or_else(|| parse_error!())? .as_str() .ok_or_else(|| parse_error!())?; @@ -176,21 +181,23 @@ fn parse_comp_result( if let Some(ref contract) = contract { if !all.contains_key(contract) { - Err(format_err!("Source file doesn't contain the desired contract \"{}\"", contract)) + Err(format_err!( + "Source file doesn't contain the desired contract \"{}\"", + contract + )) } else { Ok(all.get(contract).unwrap().clone()) } } else { - let mut iter = - all.iter().filter(|(_, v)| { - if !compile { - true - } else if let Some(v) = v.as_object() { - v.get("assembly").is_some() - } else { - false - } - }); + let mut iter = all.iter().filter(|(_, v)| { + if !compile { + true + } else if let Some(v) = v.as_object() { + v.get("assembly").is_some() + } else { + false + } + }); let qualification = if compile { "deployable " } else { "" }; let entry = iter.next(); if let Some(entry) = entry { @@ -200,7 +207,10 @@ fn parse_comp_result( Ok(entry.1.clone()) } } else { - Err(format_err!("Source file contains no {}contracts", qualification)) + Err(format_err!( + "Source file contains no {}contracts", + qualification + )) } } } @@ -216,12 +226,17 @@ pub fn build(args: Args) -> Status { if let Some(ref output_prefix) = args.output_prefix { if output_prefix.contains(std::path::is_separator) { - bail!("Invalid output prefix \"{}\". Use option -O to set output directory", output_prefix); + bail!( + "Invalid output prefix \"{}\". Use option -O to set output directory", + output_prefix + ); } } let input_canonical = dunce::canonicalize(Path::new(&args.input))?; - let input = input_canonical.as_os_str().to_str() + let input = input_canonical + .as_os_str() + .to_str() .ok_or_else(|| format_err!("Failed to get canonical path"))?; let input = if cfg!(target_family = "windows") { @@ -237,20 +252,24 @@ pub fn build(args: Args) -> Status { &res, &input, args.contract, - !(args.abi_json || args.ast_json || args.ast_compact_json) + !(args.abi_json || args.ast_json || args.ast_compact_json), )?; if args.function_ids { println!("{}", serde_json::to_string_pretty(&out["functionIds"])?); - return Ok(()) + return Ok(()); } if args.private_function_ids { - println!("{}", serde_json::to_string_pretty(&out["privateFunctionIds"])?); - return Ok(()) + println!( + "{}", + serde_json::to_string_pretty(&out["privateFunctionIds"])? + ); + return Ok(()); } - let input_file_stem = input_canonical.file_stem() + let input_file_stem = input_canonical + .file_stem() .ok_or_else(|| format_err!("Failed to extract file stem"))? .to_str() .ok_or_else(|| format_err!("Failed to get file stem"))? @@ -259,14 +278,15 @@ pub fn build(args: Args) -> Status { let output_tvc = format!("{}.tvc", output_prefix); if args.ast_json || args.ast_compact_json { - let all = res.as_object() + let all = res + .as_object() .ok_or_else(|| parse_error!())? .get("sources") .ok_or_else(|| parse_error!())? .as_object() .ok_or_else(|| parse_error!())?; - let mut array = vec!(); + let mut array = vec![]; for (_, val) in all { let ast = val .as_object() @@ -286,7 +306,7 @@ pub fn build(args: Args) -> Status { serde_json::to_writer(&mut ast_file, &ast)?; } writeln!(ast_file)?; - return Ok(()) + return Ok(()); } let abi = &out["abi"]; @@ -294,7 +314,7 @@ pub fn build(args: Args) -> Status { let mut abi_file = File::create(output_path.join(&abi_file_name))?; printer::print_abi_json_canonically(&mut abi_file, abi)?; if args.abi_json { - return Ok(()) + return Ok(()); } let assembly = out["assembly"] @@ -308,15 +328,23 @@ pub fn build(args: Args) -> Status { let mut inputs = Vec::new(); if let Some(lib) = args.lib { let lib_file = File::open(&lib)?; - inputs.push(ParseEngineInput { buf: Box::new(lib_file), name: lib }); + inputs.push(ParseEngineInput { + buf: Box::new(lib_file), + name: lib, + }); } else { - inputs.push(ParseEngineInput { buf: Box::new(STDLIB), name: String::from("stdlib_sol.tvm") }); + inputs.push(ParseEngineInput { + buf: Box::new(STDLIB), + name: String::from("stdlib_sol.tvm"), + }); } - inputs.push(ParseEngineInput { buf: Box::new(assembly.as_bytes()), name: format!("{}/{}", output_dir, assembly_file_name) }); + inputs.push(ParseEngineInput { + buf: Box::new(assembly.as_bytes()), + name: format!("{}/{}", output_dir, assembly_file_name), + }); let mut prog = Program::new(ParseEngine::new_generic(inputs, Some(format!("{}", abi)))?); - let output_filename = if output_dir == "." { output_tvc } else { @@ -326,11 +354,7 @@ pub fn build(args: Args) -> Status { prog.set_silent(args.silent); prog.set_print_code(args.print_code); - prog.compile_to_file_ex( - -1, - Some(&output_filename), - None, - )?; + prog.compile_to_file_ex(-1, Some(&output_filename), None)?; if !args.print_code { let mut dbg_file = File::create(format!("{}/{}.debug.json", output_dir, output_prefix))?; serde_json::to_writer_pretty(&mut dbg_file, &prog.dbgmap)?; diff --git a/sold/src/main.rs b/sold/src/main.rs index e30be2a..46fee19 100644 --- a/sold/src/main.rs +++ b/sold/src/main.rs @@ -12,7 +12,7 @@ */ use clap::Parser; -use sold_lib::{Args, VERSION, build, solidity_version}; +use sold_lib::{build, solidity_version, Args, VERSION}; mod libsolc; diff --git a/sold/src/printer.rs b/sold/src/printer.rs index b37b895..fdfc95b 100644 --- a/sold/src/printer.rs +++ b/sold/src/printer.rs @@ -20,7 +20,9 @@ use serde::Serialize; use ton_types::{Result, Status}; pub fn print_abi_json_canonically(out: &mut File, value: &serde_json::Value) -> Status { - let root = value.as_object().ok_or_else(|| format_err!("ABI parsing failed"))?; + let root = value + .as_object() + .ok_or_else(|| format_err!("ABI parsing failed"))?; writeln!(out, "{{")?; writeln!(out, "\t\"ABI version\": {},", root["ABI version"])?; if let Some(version) = root.get("version") { @@ -28,7 +30,9 @@ pub fn print_abi_json_canonically(out: &mut File, value: &serde_json::Value) -> } if let Some(header) = root.get("header") { write!(out, "\t\"header\": [")?; - let array = header.as_array().ok_or_else(|| format_err!("ABI parsing failed"))?; + let array = header + .as_array() + .ok_or_else(|| format_err!("ABI parsing failed"))?; for i in 0..array.len() { write!(out, "{}", array[i])?; if i + 1 != array.len() { @@ -63,7 +67,9 @@ pub fn print_abi_json_canonically(out: &mut File, value: &serde_json::Value) -> } fn print_data(out: &mut File, value: &serde_json::Value) -> Status { - let json = value.as_array().ok_or_else(|| format_err!("ABI parsing failed"))?; + let json = value + .as_array() + .ok_or_else(|| format_err!("ABI parsing failed"))?; for f in 0..json.len() { write!(out, "\t\t")?; @@ -78,9 +84,13 @@ fn print_data(out: &mut File, value: &serde_json::Value) -> Status { } fn print(out: &mut File, value: &serde_json::Value) -> Status { - let json = value.as_array().ok_or_else(|| format_err!("ABI parsing failed"))?; + let json = value + .as_array() + .ok_or_else(|| format_err!("ABI parsing failed"))?; for f in 0..json.len() { - let function = json[f].as_object().ok_or_else(|| format_err!("ABI parsing failed"))?; + let function = json[f] + .as_object() + .ok_or_else(|| format_err!("ABI parsing failed"))?; writeln!(out, "\t\t{{")?; writeln!(out, "\t\t\t\"name\": {},", function["name"])?; @@ -91,7 +101,9 @@ fn print(out: &mut File, value: &serde_json::Value) -> Status { writeln!(out, "\t\t\t\"inputs\": [")?; if let Some(inputs) = function.get("inputs") { - let array = inputs.as_array().ok_or_else(|| format_err!("ABI parsing failed"))?; + let array = inputs + .as_array() + .ok_or_else(|| format_err!("ABI parsing failed"))?; for i in 0..array.len() { write!(out, "\t\t\t\t")?; write!(out, "{}", to_string_pretty_no_indent(&array[i])?)?; @@ -106,7 +118,9 @@ fn print(out: &mut File, value: &serde_json::Value) -> Status { writeln!(out, "\t\t\t\"outputs\": [")?; if let Some(outputs) = function.get("outputs") { - let array = outputs.as_array().ok_or_else(|| format_err!("ABI parsing failed"))?; + let array = outputs + .as_array() + .ok_or_else(|| format_err!("ABI parsing failed"))?; for o in 0..array.len() { write!(out, "\t\t\t\t")?; write!(out, "{}", to_string_pretty_no_indent(&array[o])?)?; diff --git a/sold/tests/tests.rs b/sold/tests/tests.rs index 98a4846..d79a624 100644 --- a/sold/tests/tests.rs +++ b/sold/tests/tests.rs @@ -11,17 +11,17 @@ * limitations under the License. */ -use predicates::prelude::*; use assert_cmd::Command; +use predicates::prelude::*; type Status = Result<(), Box>; const BIN_NAME: &str = "sold"; fn remove_all_outputs(name: &str) -> Status { - std::fs::remove_file(format!("tests/{}.abi.json", name))?; - std::fs::remove_file(format!("tests/{}.code", name))?; + std::fs::remove_file(format!("tests/{}.abi.json", name))?; + std::fs::remove_file(format!("tests/{}.code", name))?; std::fs::remove_file(format!("tests/{}.debug.json", name))?; - std::fs::remove_file(format!("tests/{}.tvc", name))?; + std::fs::remove_file(format!("tests/{}.tvc", name))?; Ok(()) } @@ -103,7 +103,9 @@ fn test_library() -> Status { .arg("tests") .assert() .failure() - .stderr(predicate::str::contains("Source file contains no deployable contracts")); + .stderr(predicate::str::contains( + "Source file contains no deployable contracts", + )); Ok(()) } @@ -176,7 +178,8 @@ fn test_private_function_ids() -> Status { .arg("--private-function-ids") .assert() .success() - .stdout(predicate::str::contains(r#"[ + .stdout(predicate::str::contains( + r#"[ { "id": 4199241165, "scope": "C", @@ -197,6 +200,7 @@ fn test_private_function_ids() -> Status { "scope": "Math", "sign": "mul(uint256,uint256)" } -"#)); +"#, + )); Ok(()) } From fc9cafaeaa199e2384c7a161a4e986eac46dbab1 Mon Sep 17 00:00:00 2001 From: Ilyar <761285+ilyar@users.noreply.github.com> Date: Fri, 14 Oct 2022 04:55:08 +0200 Subject: [PATCH 02/13] fix specified in conflicts with does not exist --- sold/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sold/src/lib.rs b/sold/src/lib.rs index 3918641..3299e8e 100644 --- a/sold/src/lib.rs +++ b/sold/src/lib.rs @@ -417,10 +417,10 @@ pub struct Args { #[clap(long, value_parser)] pub private_function_ids: bool, /// Get AST of all source files in JSON format - #[clap(long, value_parser, conflicts_with = "ast-compact-json")] + #[clap(long, value_parser, conflicts_with = "ast_compact_json")] pub ast_json: bool, /// Get AST of all source files in compact JSON format - #[clap(long, value_parser, conflicts_with = "ast-json")] + #[clap(long, value_parser, conflicts_with = "ast_json")] pub ast_compact_json: bool, /// Get ABI without actually compiling #[clap(long, value_parser)] From 88cf46b7ea78dca24abaeae589ffc7eac92489a6 Mon Sep 17 00:00:00 2001 From: Ilyar <761285+ilyar@users.noreply.github.com> Date: Fri, 14 Oct 2022 04:55:08 +0200 Subject: [PATCH 03/13] setup DI --- .github/workflows/release.yml | 93 ++++++++++++++++++++++++++++ .gitignore | 2 + Cargo.toml | 3 + Makefile | 35 +++++++++++ compiler/scripts/install_deps.sh | 10 +-- script/toolchain.sh | 101 +++++++++++++++++++++++++++++++ script/util.sh | 98 ++++++++++++++++++++++++++++++ sold/Cargo.toml | 1 - 8 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 Makefile create mode 100644 script/toolchain.sh create mode 100644 script/util.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4de9ea8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,93 @@ +name: Release + +on: + push: + tags: + - '*' + +defaults: + run: + shell: bash + +jobs: + qa: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: rustfmt, clippy + override: true + + - uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + + - run: | + make qa + + build: + needs: + - qa + runs-on: ${{ matrix.platform.on }} + name: ${{ matrix.platform.name }} + strategy: + fail-fast: false + matrix: + platform: + - { on: ubuntu-20.04, name: linux-amd64, target: x86_64-unknown-linux-musl } + - { on: ubuntu-20.04, name: freebsd-amd64, target: x86_64-unknown-freebsd } + - { on: macos-11, name: darwin-amd64, target: x86_64-apple-darwin } + - { on: macos-11, name: darwin-arm64, target: aarch64-apple-darwin } + - { on: windows-2022, name: windows-amd64, target: x86_64-pc-windows-gnu } + + steps: + - uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.platform.target }} + profile: minimal + override: true + + - uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-release-${{ hashFiles('Cargo.lock') }} + + - run: | + make release + tar -czvf sold-${{ github.ref_name }}-${{ matrix.platform.name }}.tar.gz -C release sold* + + - uses: actions/upload-artifact@v3 + with: + name: sold-${{ github.ref_name }}-${{ matrix.platform.name }} + path: | + release/sold* + + - uses: softprops/action-gh-release@v1 + if: ${{ startsWith(github.ref, 'refs/tags/') }} + with: + draft: false + files: | + sold-${{ github.ref_name }}-${{ matrix.platform.name }}.tar.gz + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 1457516..68ea87e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ build/ sold/target/ target/ .idea/ +.deps-ready +release diff --git a/Cargo.toml b/Cargo.toml index b3eb5ca..6da6461 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,3 +2,6 @@ members = [ 'sold' ] + +[profile.release] +strip = true # Automatically strip symbols from the binary. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f47fc54 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +UNAME := $(shell uname) + +ifeq ($(UNAME), win) +TARGET = sold.exe +else +TARGET = sold +endif + +.deps-ready: + bash script/toolchain.sh -s + touch .deps-ready + +clean: + rm -fr release + cargo clean + +test: .deps-ready + cargo test + +fmt: + cargo fmt + +lint: .deps-ready + cargo fmt --all -- --check + cargo clippy --all-targets + +qa: lint test + +target/release/$(TARGET): .deps-ready + cargo build --release + +release: target/release/$(TARGET) + mkdir -p release + cp target/release/$(TARGET) release/ + diff --git a/compiler/scripts/install_deps.sh b/compiler/scripts/install_deps.sh index 0b68620..93a857f 100755 --- a/compiler/scripts/install_deps.sh +++ b/compiler/scripts/install_deps.sh @@ -93,14 +93,14 @@ case $(uname -s) in 10.15) echo "Installing solidity dependencies on macOS 10.15 Catalina." ;; - 11.0 | 11.1 | 11.2 | 11.3 | 11.4) - echo "Installing solidity dependencies on macOS 11.0 / 11.1 / 11.2 / 11.3 / 11.4 Big Sur." + 11.*) + echo "Installing solidity dependencies on macOS 11.x Big Sur." ;; 12.*) echo "Installing solidity dependencies on macOS 12 Monterey." ;; *) - echo "Unsupported macOS version." + echo "Unsupported macOS version: $(sw_vers -productVersion)" echo "We only support Mavericks, Yosemite, El Capitan, Sierra, High Sierra, Mojave, Catalina, Big Sur and Monterey." exit 1 ;; @@ -177,7 +177,7 @@ case $(uname -s) in Debian*) #Debian . /etc/os-release - + # Install "normal packages" sudo apt-get -y update sudo apt-get -y install \ @@ -254,7 +254,7 @@ case $(uname -s) in Ubuntu|LinuxMint) #LinuxMint is a distro on top of Ubuntu. #Ubuntu - + sudo apt-get -y update sudo apt-get -y install \ build-essential \ diff --git a/script/toolchain.sh b/script/toolchain.sh new file mode 100644 index 0000000..c77047f --- /dev/null +++ b/script/toolchain.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash + +set -o errexit + +projectDir=$(cd "$(dirname "${0}")/.." && pwd) +# shellcheck source=script/util.sh +source "${projectDir}/script/util.sh" || source ./util.sh + +usage() { + println "POSIX-compliant bash script to manage toolchain for develop project" + println "Usage: ${0}