diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..e0a5f1d06 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,304 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake3" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pbr" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_cookbook" +version = "0.1.0" +dependencies = [ + "blake3 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pbr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +"checksum blake3 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "68df31bdf2bbb567e5adf8f21ac125dc0e897b1381e7b841f181521f06fc3134" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +"checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" +"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum pbr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4403eb718d70c03ee279e51737782902c68cca01e870a33b6a2f9dfb50b9cd83" +"checksum proc-macro2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639" +"checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +"checksum serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" +"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" +"checksum syn 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac" +"checksum termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905" +"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..5b297bc9f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "redox_cookbook" +version = "0.1.0" +authors = ["Jeremy Soller "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "cook" +path = "src/bin/cook.rs" + +[lib] +name = "cookbook" +path = "src/lib.rs" + +[dependencies] +blake3 = "0.3.3" +pbr = "1.0.2" +serde = { version = "1.0", features = ["derive"] } +sha2 = "0.8.1" +termion = "1.5.5" +toml = "0.5.6" diff --git a/src/bin/cook.rs b/src/bin/cook.rs new file mode 100644 index 000000000..aebe86841 --- /dev/null +++ b/src/bin/cook.rs @@ -0,0 +1,302 @@ +use cookbook::blake3::blake3_progress; +use cookbook::recipe::{Recipe, SourceRecipe, BuildRecipe}; +use cookbook::sha256::sha256_progress; +use std::{ + env, + fs, + path::Path, + process::{self, Command}, +}; +use termion::{color, style}; + +fn create_dir_clean(dir: &Path) -> Result<(), String> { + if dir.is_dir() { + // Remove previous directory + fs::remove_dir_all(&dir).map_err(|err| format!( + "failed to remove '{}': {}\n{:?}", + dir.display(), + err, + err + ))?; + } + // directory + fs::create_dir(&dir).map_err(|err| format!( + "failed to create '{}': {}\n{:?}", + dir.display(), + err, + err + )) +} + +fn rename(src: &Path, dst: &Path) -> Result<(), String> { + fs::rename(src, dst).map_err(|err| format!( + "failed to rename '{}' to '{}': {}\n{:?}", + src.display(), + dst.display(), + err, + err + )) +} + +fn run_command(mut command: process::Command) -> Result<(), String> { + let status = command.status().map_err(|err| format!( + "failed to run {:?}: {}\n{:#?}", + command, + err, + err + ))?; + + if ! status.success() { + return Err(format!( + "failed to run {:?}: exited with status {}", + command, + status + )); + } + + Ok(()) +} + +fn fetch(source: &SourceRecipe, recipe_dir: &Path) -> Result<(), String> { + let source_dir = recipe_dir.join("source"); + match source { + SourceRecipe::Git { git, upstream, branch, rev } => { + if ! source_dir.is_dir() { + // Create source.tmp + let source_dir_tmp = recipe_dir.join("source.tmp"); + create_dir_clean(&source_dir_tmp)?; + + // Clone the repository to source.tmp + let mut command = Command::new("git"); + command.arg("clone").arg("--recursive").arg(&git); + if let Some(branch) = branch { + command.arg("--branch").arg(&branch); + } + command.arg(&source_dir); + run_command(command)?; + + // Move source.tmp to source atomically + rename(&source_dir_tmp, &source_dir)?; + } else { + // Reset origin + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("remote").arg("set-url").arg("origin").arg(&git); + run_command(command)?; + + // Fetch origin + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("fetch").arg("origin"); + run_command(command)?; + } + + if let Some(upstream) = upstream { + //TODO: set upstream URL + // git remote set-url upstream "$GIT_UPSTREAM" &> /dev/null || + // git remote add upstream "$GIT_UPSTREAM" + // git fetch upstream + } + + if let Some(rev) = rev { + // Check out specified revision + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("checkout").arg(&rev); + run_command(command)?; + } else { + //TODO: complicated stuff to check and reset branch to origin + // ORIGIN_BRANCH="$(git branch --remotes | grep '^ origin/HEAD -> ' | cut -d ' ' -f 5-)" + // if [ -n "$BRANCH" ] + // then + // ORIGIN_BRANCH="origin/$BRANCH" + // fi + // + // if [ "$(git rev-parse HEAD)" != "$(git rev-parse $ORIGIN_BRANCH)" ] + // then + // git checkout -B "$(echo "$ORIGIN_BRANCH" | cut -d / -f 2-)" "$ORIGIN_BRANCH" + // fi + } + + // Sync submodules URL + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("submodule").arg("sync").arg("--recursive"); + run_command(command)?; + + // Update submodules + let mut command = Command::new("git"); + command.arg("-C").arg(&source_dir); + command.arg("submodule").arg("update").arg("--init").arg("--recursive"); + run_command(command)?; + }, + SourceRecipe::Tar { tar, blake3, sha256 } => { + if ! source_dir.is_dir() { + // Download tar + //TODO: replace wget + let source_tar = recipe_dir.join("source.tar"); + if ! source_tar.is_file() { + let source_tar_tmp = recipe_dir.join("source.tar.tmp"); + + let mut command = Command::new("wget"); + command.arg(&tar); + command.arg("-O").arg(&source_tar_tmp); + run_command(command)?; + + // Move source.tar.tmp to source.tar atomically + rename(&source_tar_tmp, &source_tar)?; + } + + if let Some(blake3) = blake3 { + //TODO + // Calculate blake3 + let source_tar_blake3 = blake3_progress(&source_tar).map_err(|err| format!( + "failed to calculate blake3 of '{}': {}\n{:?}", + source_tar.display(), + err, + err + ))?; + + // Check if it matches recipe + if &source_tar_blake3 != blake3 { + return Err(format!( + "calculated blake3 '{}' does not match recipe blake3 '{}'", + source_tar_blake3, + blake3 + )); + } + } + + if let Some(sha256) = sha256 { + // Calculate sha256 + let source_tar_sha256 = sha256_progress(&source_tar).map_err(|err| format!( + "failed to calculate sha256 of '{}': {}\n{:?}", + source_tar.display(), + err, + err + ))?; + + // Check if it matches recipe + if &source_tar_sha256 != sha256 { + return Err(format!( + "calculated sha256 '{}' does not match recipe sha256 '{}'", + source_tar_sha256, + sha256 + )); + } + } + + // Create source.tmp + let source_dir_tmp = recipe_dir.join("source.tmp"); + create_dir_clean(&source_dir_tmp)?; + + // Extract tar to source.tmp + //TODO: use tar crate (how to deal with compression?) + let mut command = Command::new("tar"); + command.arg("--extract"); + command.arg("--verbose"); + command.arg("--file").arg(&source_tar); + command.arg("--directory").arg(&source_dir_tmp); + run_command(command)?; + + // Move source.tmp to source atomically + rename(&source_dir_tmp, &source_dir)?; + } + } + } + + Ok(()) +} + +fn cook(recipe_name: &str) -> Result<(), String> { + //TODO: sanitize recipe name? + let recipe_dir = Path::new("recipes").join(recipe_name); + if ! recipe_dir.is_dir() { + return Err(format!( + "failed to find recipe directory '{}'", + recipe_dir.display() + )); + } + + let recipe_file = recipe_dir.join("recipe.toml"); + if ! recipe_file.is_file() { + return Err(format!( + "failed to find recipe file '{}'", + recipe_file.display() + )); + } + + let recipe_toml = fs::read_to_string(&recipe_file).map_err(|err| format!( + "failed to read recipe file '{}': {}\n{:#?}", + recipe_file.display(), + err, + err + ))?; + + let recipe: Recipe = toml::from_str(&recipe_toml).map_err(|err| format!( + "failed to parse recipe file '{}': {}\n{:#?}", + recipe_file.display(), + err, + err + ))?; + + fetch(&recipe.source, &recipe_dir).map_err(|err| format!( + "failed to fetch: {}", + err + ))?; + + Ok(()) +} + +fn main() { + let mut matching = true; + let mut quiet = false; + let mut recipe_names = Vec::new(); + for arg in env::args().skip(1) { + match arg.as_str() { + "--" if matching => matching = false, + "-q" | "--quiet" if matching => quiet = true, + _ => recipe_names.push(arg), + } + } + + for recipe_name in recipe_names.iter() { + if ! quiet { + eprintln!( + "{}{}cook - {}{}{}", + style::Bold, + color::Fg(color::AnsiValue(215)), + recipe_name, + color::Fg(color::Reset), + style::Reset, + ); + } + + match cook(recipe_name) { + Ok(()) => { + if ! quiet { + eprintln!( + "{}{}cook - {} - successful{}{}", + style::Bold, + color::Fg(color::AnsiValue(46)), + recipe_name, + color::Fg(color::Reset), + style::Reset, + ); + } + }, + Err(err) => { + eprintln!( + "{}{}cook - {} - error:{}{} {}", + style::Bold, + color::Fg(color::AnsiValue(196)), + recipe_name, + color::Fg(color::Reset), + style::Reset, + err, + ); + process::exit(1); + } + } + } +} diff --git a/src/blake3.rs b/src/blake3.rs new file mode 100644 index 000000000..acc037d35 --- /dev/null +++ b/src/blake3.rs @@ -0,0 +1,46 @@ +use blake3::Hasher; +use std::{ + fs, + io::{Read, Result}, + path::Path, + time::Duration, +}; + +use crate::progress_bar::{ProgressBar, ProgressBarRead}; + +pub fn blake3(r: &mut R) -> Result { + let mut hasher = Hasher::new(); + + let mut data = vec![0; 4 * 1024 * 1024]; + loop { + let count = r.read(&mut data)?; + if count == 0 { + break; + } + + hasher.update(&data[..count]); + } + + let hash = hasher.finalize(); + Ok(format!("{}", hash.to_hex())) +} + +pub fn blake3_progress>(path: P) -> Result { + let len = fs::metadata(&path)?.len(); + + let mut f = fs::File::open(&path)?; + + let mut pb = ProgressBar::new(len); + pb.message("blake3: "); + pb.set_max_refresh_rate(Some(Duration::new(1, 0))); + pb.set_units(pbr::Units::Bytes); + + let res = { + let mut pbr = ProgressBarRead::new(&mut pb, &mut f); + blake3(&mut pbr) + }; + + pb.finish_println(""); + + res +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..2fbe53530 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +pub mod blake3; +pub mod recipe; +pub mod sha256; + +mod progress_bar; diff --git a/src/progress_bar.rs b/src/progress_bar.rs new file mode 100644 index 000000000..c02b939d3 --- /dev/null +++ b/src/progress_bar.rs @@ -0,0 +1,51 @@ +pub use pbr::ProgressBar; + +use std::io::{Read, Write, Result}; + +pub struct ProgressBarRead<'p, 'r, P: Write + 'p, R: Read + 'r> { + pb: &'p mut ProgressBar

, + r: &'r mut R, +} + +impl<'p, 'r, P: Write, R: Read> ProgressBarRead<'p, 'r, P, R> { + pub fn new(pb: &'p mut ProgressBar

, r: &'r mut R) -> ProgressBarRead<'p, 'r, P, R> { + ProgressBarRead { + pb: pb, + r: r + } + } +} + +impl<'p, 'r, P: Write, R: Read> Read for ProgressBarRead<'p, 'r, P, R> { + fn read(&mut self, buf: &mut [u8]) -> Result { + let count = self.r.read(buf)?; + self.pb.add(count as u64); + Ok(count) + } +} + +pub struct ProgressBarWrite<'p, 'w, P: Write + 'p, W: Write + 'w> { + pb: &'p mut ProgressBar

, + w: &'w mut W, +} + +impl<'p, 'w, P: Write, W: Write> ProgressBarWrite<'p, 'w, P, W> { + pub fn new(pb: &'p mut ProgressBar

, w: &'w mut W) -> ProgressBarWrite<'p, 'w, P, W> { + ProgressBarWrite { + pb: pb, + w: w + } + } +} + +impl<'p, 'w, P: Write, W: Write> Write for ProgressBarWrite<'p, 'w, P, W> { + fn write(&mut self, buf: &[u8]) -> Result { + let count = self.w.write(buf)?; + self.pb.add(count as u64); + Ok(count) + } + + fn flush(&mut self) -> Result<()> { + self.w.flush() + } +} diff --git a/src/recipe.rs b/src/recipe.rs new file mode 100644 index 000000000..6ec4a918e --- /dev/null +++ b/src/recipe.rs @@ -0,0 +1,112 @@ +use serde::{Deserialize, Serialize}; + +/// Specifies how to download the source for a recipe +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(untagged)] +pub enum SourceRecipe { + /// A git repository source + Git { + /// The URL for the git repository, such as https://gitlab.redox-os.org/redox-os/ion.git + git: String, + /// The URL for an upstream repository + upstream: Option, + /// The optional branch of the git repository to track, such as master. Please specify to + /// make updates to the rev easier + branch: Option, + /// The optional revision of the git repository to use for builds. Please specify for + /// reproducible builds + rev: Option, + }, + /// A tar file source + Tar { + /// The URL of a tar source + tar: String, + /// The optional blake3 sum of the tar file. Please specify this to make reproducible + /// builds more reliable + blake3: Option, + /// The optional sha256 sum of the tar file. This is a slower alternative to a blake3 sum + sha256: Option, + }, +} + +/// Specifies how to build a recipe +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(tag = "template")] +pub enum BuildRecipe { + /// Will build and install using cargo + #[serde(rename = "cargo")] + Cargo, + /// Will build and install using custom commands + #[serde(rename = "custom")] + Custom { + script: String, + }, +} + +/// Everything required to build a Redox package +#[derive(Debug, Deserialize, PartialEq, Serialize)] +pub struct Recipe { + /// Specifies how to donload the source for this recipe + pub source: SourceRecipe, + /// A list of patch files to apply to the source + #[serde(default)] + pub patches: Vec, + /// Specifies how to build this recipe + pub build: BuildRecipe, +} + + +#[cfg(test)] +mod tests { + #[test] + fn git_cargo_recipe() { + use crate::recipe::{Recipe, SourceRecipe, BuildRecipe}; + + let recipe: Recipe = toml::from_str(r#" + [source] + git = "https://gitlab.redox-os.org/redox-os/acid.git" + branch = "master" + rev = "06344744d3d55a5ac9a62a6059cb363d40699bbc" + + [build] + template = "cargo" + "#).unwrap(); + + assert_eq!(recipe, Recipe { + source: SourceRecipe::Git { + git: "https://gitlab.redox-os.org/redox-os/acid.git".to_string(), + upstream: None, + branch: Some("master".to_string()), + rev: Some("06344744d3d55a5ac9a62a6059cb363d40699bbc".to_string()), + }, + patches: Vec::new(), + build: BuildRecipe::Cargo, + }); + } + + #[test] + fn tar_custom_recipe() { + use crate::recipe::{Recipe, SourceRecipe, BuildRecipe}; + + let recipe: Recipe = toml::from_str(r#" + [source] + tar = "http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz" + sha256 = "4f3fc6178a533d392064f14776b23c397ed4b9f48f5de297aba73b643f955c08" + + [build] + template = "custom" + script = "make" + "#).unwrap(); + + assert_eq!(recipe, Recipe { + source: SourceRecipe::Tar { + tar: "http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz".to_string(), + sha256: Some("4f3fc6178a533d392064f14776b23c397ed4b9f48f5de297aba73b643f955c08".to_string()), + }, + patches: Vec::new(), + build: BuildRecipe::Custom { + script: "make".to_string() + }, + }); + } +} diff --git a/src/sha256.rs b/src/sha256.rs new file mode 100644 index 000000000..c424e0013 --- /dev/null +++ b/src/sha256.rs @@ -0,0 +1,45 @@ +use std::{ + fs, + io::{Read, Result}, + path::Path, + time::Duration, +}; +use sha2::{Digest, Sha256}; + +use crate::progress_bar::{ProgressBar, ProgressBarRead}; + +pub fn sha256(r: &mut R) -> Result { + let mut hasher = Sha256::default(); + + let mut data = vec![0; 4 * 1024 * 1024]; + loop { + let count = r.read(&mut data)?; + if count == 0 { + break; + } + + hasher.input(&data[..count]); + } + + Ok(format!("{:x}", hasher.result())) +} + +pub fn sha256_progress>(path: P) -> Result { + let len = fs::metadata(&path)?.len(); + + let mut f = fs::File::open(&path)?; + + let mut pb = ProgressBar::new(len); + pb.message("sha256: "); + pb.set_max_refresh_rate(Some(Duration::new(1, 0))); + pb.set_units(pbr::Units::Bytes); + + let res = { + let mut pbr = ProgressBarRead::new(&mut pb, &mut f); + sha256(&mut pbr) + }; + + pb.finish_println(""); + + res +}