From 67ea61edbdcd915ae356de8a23652555255c656d Mon Sep 17 00:00:00 2001 From: guillem <4lon3ly0@tutanota.com> Date: Wed, 29 Dec 2021 23:08:28 +0100 Subject: [PATCH] Make the compiler use local source code Now the source code is compressed and statically linked into the binary, so there is no need to git clone it from github anymore, both removing version issues and the git dependency. --- .gitignore | 2 +- compiled_voila/Cargo.toml | 2 +- compiled_voila/src/main.rs | 4 +++ voila/Cargo.toml | 8 ++++-- voila/build.rs | 27 ++++++++++++++++++++ voila/src/ast/call.rs | 5 ++-- voila/src/ast/lookup.rs | 1 + voila/src/ast/script.rs | 6 ----- voila/src/bytecode.rs | 15 +++++++++++ voila/src/compiler.rs | 51 ++++++++++++++++---------------------- voila/src/lib.rs | 2 +- voila/src/main.rs | 1 - 12 files changed, 81 insertions(+), 43 deletions(-) mode change 100644 => 100755 voila/Cargo.toml create mode 100644 voila/build.rs create mode 100644 voila/src/bytecode.rs diff --git a/.gitignore b/.gitignore index 4b65b69..786ea7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ target/ test/ Cargo.lock - +code.tar.gz diff --git a/compiled_voila/Cargo.toml b/compiled_voila/Cargo.toml index 004af0a..176b462 100755 --- a/compiled_voila/Cargo.toml +++ b/compiled_voila/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -voila = {path = "../voila"} \ No newline at end of file +voila = {path = "../voila"} diff --git a/compiled_voila/src/main.rs b/compiled_voila/src/main.rs index 549c32a..a27e6d7 100755 --- a/compiled_voila/src/main.rs +++ b/compiled_voila/src/main.rs @@ -1,4 +1,8 @@ fn main() { + // the use of the `env!()` macro instead of + // its `option_env!()` counterpart is on purpose, + // so if the data needed to run is not provided, a compile-time + // error will be thrown instead of a runtime one. if let Err(ref e) = voila::exec( str_to_vec_u8(env!("v_code")).into(), // into() automatically deserializes the data std::path::PathBuf::from(env!("v_path")), diff --git a/voila/Cargo.toml b/voila/Cargo.toml old mode 100644 new mode 100755 index de9aa00..edf18c1 --- a/voila/Cargo.toml +++ b/voila/Cargo.toml @@ -27,8 +27,8 @@ keywords = [ [dependencies] md5 = "0.7.0" -sha2 = "0.10.0" -sha-1 = "0.10.0" +sha2 = "0.9.5" +sha-1 = "0.9.7" walkdir = "2.3.2" regex = "1.5.4" structopt = "0.3.23" @@ -51,6 +51,10 @@ serde = "1.0.130" serde_derive = "1.0.130" bincode = "1.3.3" +[build-dependencies] +tar = "0.4.37" +flate2 = "1.0.21" + [dev-dependencies] criterion = "0.3.5" diff --git a/voila/build.rs b/voila/build.rs new file mode 100644 index 0000000..c43b3e6 --- /dev/null +++ b/voila/build.rs @@ -0,0 +1,27 @@ +use flate2::write::GzEncoder; +use flate2::Compression; +use std::env::current_dir; +use std::fs::File; + +const COMPILE_ASSETS: [&str; 4] = ["voila", "compiled_voila", "Cargo.toml", "Cargo.lock"]; + +fn main() { + let src = ¤t_dir().expect("Can't open current directory"); + let src = src.parent().unwrap(); + let dest = File::create(format!("{}/code.tar.gz", src.to_str().unwrap())) + .expect("Can't write to current directory"); + println!("{}/code.tar.gz", src.to_str().unwrap()); + let encoder = GzEncoder::new(dest, Compression::default()); + let mut tar = tar::Builder::new(encoder); + for a in COMPILE_ASSETS { + let p = src.join(a); + if p.is_dir() { + tar.append_dir_all(a, p) + .expect("Can't read current directory"); + } else { + tar.append_path_with_name(p, a) + .expect("Can't read current directory"); + } + } + tar.finish().unwrap(); +} diff --git a/voila/src/ast/call.rs b/voila/src/ast/call.rs index e4c6d21..eef15e4 100755 --- a/voila/src/ast/call.rs +++ b/voila/src/ast/call.rs @@ -410,10 +410,11 @@ fn gzc(source: &str, dest: &str) -> Result<(), io::Error> { let mut tar = tar::Builder::new(encoder); let source = PathBuf::from(source); if source.is_dir() { - tar.append_dir_all(source.clone(), source) + tar.append_dir_all(source.clone(), source)?; } else { - tar.append_path(source) + tar.append_path(source)?; } + tar.finish() } fn gzd(source: &str, dest: &str) -> Result<(), io::Error> { diff --git a/voila/src/ast/lookup.rs b/voila/src/ast/lookup.rs index bd8d510..9037568 100755 --- a/voila/src/ast/lookup.rs +++ b/voila/src/ast/lookup.rs @@ -132,6 +132,7 @@ trait Specifier { fn detect(source: &str) -> Option; } +#[allow(clippy::enum_variant_names)] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum SizeLabel { TeraBytes, diff --git a/voila/src/ast/script.rs b/voila/src/ast/script.rs index 02a4380..7567877 100755 --- a/voila/src/ast/script.rs +++ b/voila/src/ast/script.rs @@ -45,9 +45,3 @@ pub fn run_script( // }); // } } - -impl<'source> From> for Script<'source> { - fn from(s: Vec) -> Self { - bincode::deserialize(&s[..]).unwrap() - } -} diff --git a/voila/src/bytecode.rs b/voila/src/bytecode.rs new file mode 100644 index 0000000..7c854e4 --- /dev/null +++ b/voila/src/bytecode.rs @@ -0,0 +1,15 @@ +use crate::ast::Script; + +pub type VoilaByteCode = Vec; + +impl<'source> From> for VoilaByteCode { + fn from(code: Script<'source>) -> Self { + bincode::serialize(&code).unwrap() + } +} + +impl<'source> From for Script<'source> { + fn from(s: VoilaByteCode) -> Self { + bincode::deserialize(&s[..]).unwrap() + } +} diff --git a/voila/src/compiler.rs b/voila/src/compiler.rs index b9ac58a..e2835d2 100755 --- a/voila/src/compiler.rs +++ b/voila/src/compiler.rs @@ -1,43 +1,36 @@ +use flate2::read::GzDecoder; use std::env; use std::fs; use std::path::Path; use std::process; +use tar::Archive; -/// Compiles with cargo nightly a crate in a given path -/// providing variables needed to run voila (e.g script's code) -/// leaving the compiled binary in the current directory. -pub fn compile(vars: [&str; 3]) -> Result<(), &str> { - // save current dir - let pwd = env::current_dir().unwrap(); +// Error messages +const COMPILER_ERR_MSG: &str = "Could not compile. Make sure Cargo (a wrapper over rust's compiler) is installed, it's in your PATH and the nightly toolchain is installed:\nhttps://www.rust-lang.org/tools/install"; +const TEMPDIR_ERR_MSG: &str = "Can't write to temporary directory"; +const ENV_READ_ERR_MSG: &str = "Can't access current directory"; +const ENV_WRITE_ERR_MSG: &str = "Can't change environment"; - // set git clone path +/// Embeds a Voila Script into a binary through +/// the `compiled_voila` crate. The source is +/// statically linked into the binary. +pub fn compile(vars: [&str; 3]) -> Result<(), &str> { + let source = include_bytes!("../../code.tar.gz").as_ref(); + let pwd = env::current_dir().map_err(|_| ENV_READ_ERR_MSG)?; let target_dir = &get_target_dir(); + let mut archive = Archive::new(GzDecoder::new(source)); - // delete target dir (if exists) + // prepare target dir if Path::new(target_dir).exists() { - fs::remove_dir_all(target_dir).unwrap(); - } - - // clone repo - const GIT_ERR_MSG: &str = "Could not download required files to compile the code. Make sure git is installed and in your PATH:\nhttps://git-scm.com/"; - let git_exit_status = process::Command::new("git") - .arg("clone") - .arg("https://github.com/Alonely0/Voila.git") - .arg(target_dir) - .status() - .map_err(|_| GIT_ERR_MSG)? - .code() - .unwrap_or(0); - - if git_exit_status > 0 { - return Err(GIT_ERR_MSG); + fs::remove_dir_all(target_dir).map_err(|_| TEMPDIR_ERR_MSG)?; } + fs::create_dir_all(target_dir).map_err(|_| TEMPDIR_ERR_MSG)?; + archive.unpack(target_dir).map_err(|_| TEMPDIR_ERR_MSG)?; // set dir so cargo knows where to run - env::set_current_dir(target_dir).unwrap(); + env::set_current_dir(target_dir).map_err(|_| ENV_WRITE_ERR_MSG)?; // launch compiler & get exit code - const COMPILER_ERR_MSG: &str = "Could not compile. Make sure Cargo (a wrapper over rust's compiler) is installed, it's in your PATH and the nightly toolchain is installed:\nhttps://www.rust-lang.org/tools/install"; let compiler_exit_status = process::Command::new("cargo") .env("v_code", vars[0]) .env("v_path", vars[1]) @@ -54,10 +47,10 @@ pub fn compile(vars: [&str; 3]) -> Result<(), &str> { .unwrap_or(0); // restore to the actual current directory - env::set_current_dir(pwd).unwrap(); + env::set_current_dir(pwd).map_err(|_| ENV_WRITE_ERR_MSG)?; // remove temporary files - fs::remove_dir_all(target_dir).unwrap(); + // fs::remove_dir_all(target_dir).map_err(|_| "Can't write to temporary directory")?; if compiler_exit_status > 0 { Err(COMPILER_ERR_MSG) @@ -66,7 +59,7 @@ pub fn compile(vars: [&str; 3]) -> Result<(), &str> { } } -fn get_target_dir() -> String { +pub fn get_target_dir() -> String { #[cfg(unix)] return "/tmp/Voila".to_string(); #[cfg(windows)] diff --git a/voila/src/lib.rs b/voila/src/lib.rs index 140a362..bb40e82 100755 --- a/voila/src/lib.rs +++ b/voila/src/lib.rs @@ -1,5 +1,4 @@ #![forbid(unsafe_code)] // unsafe code makes ferris get nervous -#![feature(format_args_capture)] #![feature(once_cell)] #![feature(decl_macro)] #![feature(never_type)] @@ -8,6 +7,7 @@ use std::error::Error; mod ast; +mod bytecode; mod cli; mod error; mod interpreter; diff --git a/voila/src/main.rs b/voila/src/main.rs index a35916b..ac6bf88 100644 --- a/voila/src/main.rs +++ b/voila/src/main.rs @@ -1,5 +1,4 @@ #![forbid(unsafe_code)] // unsafe code makes ferris get nervous -#![feature(format_args_capture)] #![feature(decl_macro)] #![allow(clippy::upper_case_acronyms)]