// Copyright 2021 Axiom-Team // // This file is part of Substrate-Libre-Currency. // // Substrate-Libre-Currency is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, version 3 of the License. // // Substrate-Libre-Currency is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. mod gen_calls_doc; use anyhow::{Context, Result}; use clap::Parser; use std::io::{BufReader, BufWriter, Read, Write}; use std::path::{Path, PathBuf}; use std::process::Command; const MIN_RUST_VERSION: &str = "1.58.0"; #[derive(Debug, clap::Parser)] struct DuniterXTask { #[clap(subcommand)] command: DuniterXTaskCommand, } #[derive(Debug, clap::Subcommand)] enum DuniterXTaskCommand { /// Build duniter binary Build { #[clap(long)] production: bool, }, /// Generate calls documentation GenCallsDoc, /// Inject runtime code in raw specs InjectRuntimeCode { #[clap(short, long, parse(from_os_str))] /// Runtime filepath runtime: PathBuf, #[clap(short = 's', long, parse(from_os_str))] /// Raw spec filepath raw_spec: PathBuf, }, /// Execute unit tests and integration tests /// End2tests are skipped Test, } fn main() -> Result<()> { let args = DuniterXTask::parse(); if !version_check::is_min_version(MIN_RUST_VERSION).unwrap_or(false) && exec_should_success(Command::new("rustup").args(&["update", "stable"])).is_err() { eprintln!( "Duniter requires stable Rust {} or higher. If you installed the Rust toolchain via rustup, please execute the command `rustup update stable`.", MIN_RUST_VERSION ); std::process::exit(1); } Command::new("rustc").arg("--version").status()?; Command::new("cargo").arg("--version").status()?; match args.command { DuniterXTaskCommand::Build { production } => build(production), DuniterXTaskCommand::GenCallsDoc => gen_calls_doc::gen_calls_doc(), DuniterXTaskCommand::InjectRuntimeCode { runtime, raw_spec } => { inject_runtime_code(&raw_spec, &runtime) } DuniterXTaskCommand::Test => test(), } } fn inject_runtime_code(raw_spec: &Path, runtime: &Path) -> Result<()> { // Read runtime code // SAFETY: `mmap` is fundamentally unsafe since technically the file can change // underneath us while it is mapped; in practice it's unlikely to be a problem let mut file = std::fs::File::open(runtime).with_context(|| "Failed to open runtime wasm file")?; let mut runtime_code = unsafe { memmap2::Mmap::map(&file).with_context(|| "Failed to read runtime wasm file")? }; // Read raw spec let mut file = std::fs::File::open(raw_spec).with_context(|| "Failed to open raw spec file")?; let reader = BufReader::new(file); let mut json: serde_json::Value = serde_json::from_reader(reader).with_context(|| "Failed to read raw spec file")?; println!("json raw specs loaded!"); let mut hex_runtime_code = String::with_capacity(2 + (runtime_code.len() * 2)); hex_runtime_code.push('0'); hex_runtime_code.push('x'); hex_runtime_code.push_str(&hex::encode(runtime_code)); //hex::encode_to_slice(runtime_code, &mut hex_runtime_code[2..]) //.with_context(|| "fail to convert runtime code to hex")?; const CODE_KEY: &str = "0x3a636f6465"; json.as_object_mut() .with_context(|| "invalid raw spec file")? .get_mut("genesis") .with_context(|| "invalid raw spec file: missing field genesis")? .as_object_mut() .with_context(|| "invalid raw spec file")? .get_mut("raw") .with_context(|| "invalid raw spec file: missing field raw")? .as_object_mut() .with_context(|| "invalid raw spec file")? .get_mut("top") .with_context(|| "invalid raw spec file: missing field top")? .as_object_mut() .with_context(|| "invalid raw spec file")? .insert( CODE_KEY.to_owned(), serde_json::Value::String(unsafe { std::mem::transmute(hex_runtime_code) }), ); // Write modified raw specs let mut file = std::fs::File::create(raw_spec)?; serde_json::to_writer_pretty(BufWriter::new(file), &json) .with_context(|| "fail to write raw specs")?; Ok(()) } fn build(_production: bool) -> Result<()> { exec_should_success(Command::new("cargo").args(&["clean", "-p", "duniter"]))?; exec_should_success(Command::new("cargo").args(&["build", "--locked"]))?; exec_should_success(Command::new("mkdir").args(&["build"]))?; exec_should_success(Command::new("mv").args(&["target/debug/duniter", "build/duniter"]))?; Ok(()) } fn test() -> Result<()> { exec_should_success(Command::new("cargo").args(&[ "test", "--workspace", "--exclude", "duniter-end2end-tests", ]))?; Ok(()) } fn exec_should_success(command: &mut Command) -> Result<()> { if !command.status()?.success() { std::process::exit(1); } else { Ok(()) } }