From a5a7c294a6817b3c94735c1d5e016559866db7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=2E=20Urba=C5=84czyk?= Date: Thu, 13 Oct 2022 15:21:35 +0200 Subject: [PATCH] splitting most basic logic into a separate enso-build-base crate --- Cargo.lock | 28 ++- build/base/Cargo.toml | 15 ++ build/base/src/extensions.rs | 11 + .../src/extensions/from_string.rs | 3 + .../src/extensions/future.rs | 8 - .../src/extensions/iterator.rs | 0 .../{ci_utils => base}/src/extensions/maps.rs | 0 .../{ci_utils => base}/src/extensions/path.rs | 10 + .../src/extensions/result.rs | 30 +++ .../{ci_utils => base}/src/extensions/str.rs | 0 build/base/src/fs.rs | 217 ++++++++++++++++ build/base/src/fs/wrappers.rs | 81 ++++++ build/base/src/lib.rs | 99 ++++++++ build/build/src/engine/context.rs | 1 + build/build/src/env.rs | 3 +- build/build/src/release.rs | 2 + build/build/src/repo.rs | 7 + build/build/src/repo/cloud.rs | 2 + build/ci_utils/Cargo.toml | 3 +- build/ci_utils/src/anyhow.rs | 46 ---- build/ci_utils/src/extensions.rs | 7 - build/ci_utils/src/fs.rs | 233 ++---------------- build/ci_utils/src/fs/wrappers.rs | 77 ------ build/ci_utils/src/fs/wrappers/tokio.rs | 1 + build/ci_utils/src/future.rs | 7 + build/ci_utils/src/github.rs | 1 + build/ci_utils/src/github/release.rs | 5 +- build/ci_utils/src/github/repo.rs | 2 + build/ci_utils/src/lib.rs | 77 +----- build/ci_utils/src/models/config.rs | 3 +- build/ci_utils/src/programs/seven_zip.rs | 51 ++-- build/ci_utils/src/programs/vswhere.rs | 10 +- build/cli/Cargo.toml | 1 + build/cli/src/arg.rs | 2 +- build/cli/src/arg/ide.rs | 2 + 35 files changed, 559 insertions(+), 486 deletions(-) create mode 100644 build/base/Cargo.toml create mode 100644 build/base/src/extensions.rs rename build/{ci_utils => base}/src/extensions/from_string.rs (76%) rename build/{ci_utils => base}/src/extensions/future.rs (92%) rename build/{ci_utils => base}/src/extensions/iterator.rs (100%) rename build/{ci_utils => base}/src/extensions/maps.rs (100%) rename build/{ci_utils => base}/src/extensions/path.rs (88%) rename build/{ci_utils => base}/src/extensions/result.rs (69%) rename build/{ci_utils => base}/src/extensions/str.rs (100%) create mode 100644 build/base/src/fs.rs create mode 100644 build/base/src/fs/wrappers.rs create mode 100644 build/base/src/lib.rs delete mode 100644 build/ci_utils/src/anyhow.rs diff --git a/Cargo.lock b/Cargo.lock index 6477fffbc2b8..a0bc3c94a5a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1940,6 +1940,20 @@ dependencies = [ "zip 0.6.2", ] +[[package]] +name = "enso-build-base" +version = "0.1.0" +dependencies = [ + "anyhow", + "fn-error-context", + "futures 0.3.24", + "futures-util", + "serde", + "serde_json", + "serde_yaml 0.9.13", + "tracing", +] + [[package]] name = "enso-build-cli" version = "0.1.0" @@ -1950,6 +1964,7 @@ dependencies = [ "clap 3.1.15", "derivative", "enso-build", + "enso-build-base", "enso-formatter", "futures 0.3.24", "futures-util", @@ -3793,7 +3808,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5617e92fc2f2501c3e2bc6ce547cad841adba2bae5b921c7e52510beca6d084c" dependencies = [ - "base64 0.10.1", + "base64 0.13.0", "bytes 1.1.0", "http", "httpdate", @@ -3836,10 +3851,10 @@ dependencies = [ "derivative", "derive_more", "dirs", + "enso-build-base", "filetime", "flate2", "flume", - "fn-error-context", "fs_extra", "futures 0.3.24", "futures-util", @@ -3878,7 +3893,6 @@ dependencies = [ "serde_yaml 0.9.13", "sha2", "shrinkwraprs 0.3.0", - "snafu", "strum", "symlink", "syn", @@ -6573,18 +6587,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", diff --git a/build/base/Cargo.toml b/build/base/Cargo.toml new file mode 100644 index 000000000000..e74d0c59b2a1 --- /dev/null +++ b/build/base/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "enso-build-base" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +anyhow = "1.0.65" +fn-error-context = "0.2.0" +futures-util = "0.3.24" +futures = "0.3.24" +serde = "1.0.145" +serde_json = "1.0.85" +serde_yaml = "0.9.13" +tracing = "0.1.36" diff --git a/build/base/src/extensions.rs b/build/base/src/extensions.rs new file mode 100644 index 000000000000..2ef40c162cb3 --- /dev/null +++ b/build/base/src/extensions.rs @@ -0,0 +1,11 @@ +// ============== +// === Export === +// ============== + +pub mod from_string; +pub mod future; +pub mod iterator; +pub mod maps; +pub mod path; +pub mod result; +pub mod str; diff --git a/build/ci_utils/src/extensions/from_string.rs b/build/base/src/extensions/from_string.rs similarity index 76% rename from build/ci_utils/src/extensions/from_string.rs rename to build/base/src/extensions/from_string.rs index cf2b814a1fca..4ceb59724d34 100644 --- a/build/ci_utils/src/extensions/from_string.rs +++ b/build/base/src/extensions/from_string.rs @@ -5,9 +5,12 @@ use std::any::type_name; +/// An equivalent of standard's library `std::str::FromStr` trait, but with nice error messages. pub trait FromString: Sized { + /// Parse a string into a value of this type. See: `std::str::FromStr::from_str`. fn from_str(s: &str) -> Result; + /// Parse a string into a value of this type and then convert it to `R`. fn parse_into(text: impl AsRef) -> Result where Self: TryInto, diff --git a/build/ci_utils/src/extensions/future.rs b/build/base/src/extensions/future.rs similarity index 92% rename from build/ci_utils/src/extensions/future.rs rename to build/base/src/extensions/future.rs index c147e2a103d8..77916a494d8b 100644 --- a/build/ci_utils/src/extensions/future.rs +++ b/build/base/src/extensions/future.rs @@ -65,14 +65,6 @@ pub trait TryFutureExt: TryFuture { impl TryFutureExt for T where T: TryFuture {} -pub fn receiver_to_stream( - mut receiver: tokio::sync::mpsc::Receiver, -) -> impl Stream { - futures::stream::poll_fn(move |ctx| receiver.poll_recv(ctx)) -} - - - pub trait TryStreamExt: TryStream { fn anyhow_err(self) -> stream::MapErr anyhow::Error> where diff --git a/build/ci_utils/src/extensions/iterator.rs b/build/base/src/extensions/iterator.rs similarity index 100% rename from build/ci_utils/src/extensions/iterator.rs rename to build/base/src/extensions/iterator.rs diff --git a/build/ci_utils/src/extensions/maps.rs b/build/base/src/extensions/maps.rs similarity index 100% rename from build/ci_utils/src/extensions/maps.rs rename to build/base/src/extensions/maps.rs diff --git a/build/ci_utils/src/extensions/path.rs b/build/base/src/extensions/path.rs similarity index 88% rename from build/ci_utils/src/extensions/path.rs rename to build/base/src/extensions/path.rs index e1adc358c917..e03a3822ff10 100644 --- a/build/ci_utils/src/extensions/path.rs +++ b/build/base/src/extensions/path.rs @@ -5,6 +5,7 @@ use serde::de::DeserializeOwned; pub trait PathExt: AsRef { + /// Append multiple segments to this path. fn join_iter>(&self, segments: impl IntoIterator) -> PathBuf { let mut ret = self.as_ref().to_path_buf(); ret.extend(segments); @@ -42,18 +43,27 @@ pub trait PathExt: AsRef { } } + /// Parse this file's contents as a JSON-serialized value. #[context("Failed to deserialize file `{}` as type `{}`.", self.as_ref().display(), std::any::type_name::())] fn read_to_json(&self) -> Result { let content = crate::fs::read_to_string(self)?; serde_json::from_str(&content).anyhow_err() } + /// Write this file with a JSON-serialized value. fn write_as_json(&self, value: &T) -> Result { trace!("Writing JSON to {}.", self.as_ref().display()); let file = crate::fs::create(self)?; serde_json::to_writer(file, value).anyhow_err() } + /// Parse this file's contents as a YAML-serialized value. + fn read_to_yaml(&self) -> Result { + let content = crate::fs::read_to_string(self)?; + serde_yaml::from_str(&content).anyhow_err() + } + + /// Write this file with a YAML-serialized value. fn write_as_yaml(&self, value: &T) -> Result { trace!("Writing YAML to {}.", self.as_ref().display()); let file = crate::fs::create(self)?; diff --git a/build/ci_utils/src/extensions/result.rs b/build/base/src/extensions/result.rs similarity index 69% rename from build/ci_utils/src/extensions/result.rs rename to build/base/src/extensions/result.rs index ef8566dd2fac..8cb938f5b233 100644 --- a/build/ci_utils/src/extensions/result.rs +++ b/build/base/src/extensions/result.rs @@ -25,6 +25,18 @@ pub trait ResultExt: Sized { E: Into, T2: Send + 'a, E2: Send + 'a; + + + fn anyhow_err(self) -> Result + where E: Into; + + fn flatten_fut( + self, + ) -> futures::future::Either< + std::future::Ready>, + futures::future::IntoFuture, + > + where T: TryFuture>; } impl ResultExt for std::result::Result { @@ -62,4 +74,22 @@ impl ResultExt for std::result::Result { Err(e) => ready(Err(e.into())).right_future(), } } + + fn anyhow_err(self) -> Result + where E: Into { + self.map_err(E::into) + } + + fn flatten_fut( + self, + ) -> futures::future::Either< + std::future::Ready>, + futures::future::IntoFuture, + > + where T: TryFuture> { + match self { + Ok(fut) => fut.into_future().right_future(), + Err(e) => ready(Err(T::Error::from(e))).left_future(), + } + } } diff --git a/build/ci_utils/src/extensions/str.rs b/build/base/src/extensions/str.rs similarity index 100% rename from build/ci_utils/src/extensions/str.rs rename to build/base/src/extensions/str.rs diff --git a/build/base/src/fs.rs b/build/base/src/fs.rs new file mode 100644 index 000000000000..a16840c3d77b --- /dev/null +++ b/build/base/src/fs.rs @@ -0,0 +1,217 @@ +use crate::prelude::*; + + +// ============== +// === Export === +// ============== + +pub mod wrappers; + +pub use wrappers::*; + + + +/// Like the standard version but will create any missing parent directories from the path. +#[context("Failed to write path: {}", path.as_ref().display())] +pub fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result { + create_parent_dir_if_missing(&path)?; + wrappers::write(&path, &contents) +} + +/// Serialize the data to JSON text and write it to the file. +/// +/// See [`write`]. +#[context("Failed to write path: {}", path.as_ref().display())] +pub fn write_json(path: impl AsRef, contents: &impl Serialize) -> Result { + let contents = serde_json::to_string(contents)?; + write(&path, &contents) +} + +/// Like the standard version but will create any missing parent directories from the path. +#[context("Failed to open path for writing: {}", path.as_ref().display())] +pub fn create(path: impl AsRef) -> Result { + create_parent_dir_if_missing(&path)?; + wrappers::create(&path) +} + +#[context("Failed to read the file: {}", path.as_ref().display())] +pub fn read_string_into(path: impl AsRef) -> Result { + read_to_string(&path)?.parse2() +} + +/// Create a directory (and all missing parent directories), +/// +/// Does not fail when a directory already exists. +#[context("Failed to create directory {}", path.as_ref().display())] +pub fn create_dir_if_missing(path: impl AsRef) -> Result { + let result = std::fs::create_dir_all(&path); + match result { + Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()), + result => result.anyhow_err(), + } +} + +/// Create a parent directory for path (and all missing parent directories), +/// +/// Does not fail when a directory already exists. +#[context("Failed to create parent directory for {}", path.as_ref().display())] +pub fn create_parent_dir_if_missing(path: impl AsRef) -> Result { + if let Some(parent) = path.as_ref().parent() { + create_dir_if_missing(parent)?; + Ok(parent.into()) + } else { + bail!("No parent directory for path {}.", path.as_ref().display()) + } +} + +/// Remove a directory with all its subtree. +/// +/// Does not fail if the directory is not found. +#[tracing::instrument(fields(path = %path.as_ref().display()))] +#[context("Failed to remove directory {}", path.as_ref().display())] +pub fn remove_dir_if_exists(path: impl AsRef) -> Result { + let result = std::fs::remove_dir_all(&path); + match result { + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), + result => result.anyhow_err(), + } +} + +/// Remove a regular file. +/// +/// Does not fail if the file is not found. +#[tracing::instrument(fields(path = %path.as_ref().display()))] +#[context("Failed to remove file {}", path.as_ref().display())] +pub fn remove_file_if_exists(path: impl AsRef) -> Result<()> { + let result = std::fs::remove_file(&path); + match result { + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), + result => result.anyhow_err(), + } +} + +/// Remove a file being either directory or regular file.. +/// +/// Does not fail if the file is not found. +#[context("Failed to remove entry {} (if exists)", path.as_ref().display())] +pub fn remove_if_exists(path: impl AsRef) -> Result { + let path = path.as_ref(); + if path.is_dir() { + remove_dir_if_exists(path) + } else { + remove_file_if_exists(path) + } +} + +/// Recreate directory, so it exists and is empty. +pub fn reset_dir(path: impl AsRef) -> Result { + let path = path.as_ref(); + debug!("Will reset directory {}", path.display()); + remove_dir_if_exists(path)?; + create_dir_if_missing(path)?; + Ok(()) +} + +/// Fail if the given path does not exist. +pub fn require_exist(path: impl AsRef) -> Result { + if path.as_ref().exists() { + trace!("{} does exist.", path.as_ref().display()); + Ok(()) + } else { + bail!("{} does not exist.", path.as_ref().display()) + } +} + +/// Check if the both path are equal. +/// +/// This performs canonicalization of the paths before comparing them. As such, it requires that the +/// both paths exist. +pub fn same_existing_path(source: impl AsRef, destination: impl AsRef) -> Result { + Ok(canonicalize(source)? == canonicalize(destination)?) +} + +#[context("Failed because the path does not point to a directory: {}", path.as_ref().display())] +pub fn expect_dir(path: impl AsRef) -> Result { + let filetype = metadata(&path)?.file_type(); + if filetype.is_dir() { + Ok(()) + } else { + bail!("File is not directory, its type is: {filetype:?}") + } +} + + +#[context("Failed because the path does not point to a regular file: {}", path.as_ref().display())] +pub fn expect_file(path: impl AsRef) -> Result { + let filetype = metadata(&path)?.file_type(); + if filetype.is_file() { + Ok(()) + } else { + bail!("File is not a regular file, its type is: {filetype:?}") + } +} + +#[cfg(not(target_os = "windows"))] +#[context("Failed to update permissions on `{}`", path.as_ref().display())] +pub fn allow_owner_execute(path: impl AsRef) -> Result { + use crate::anyhow::ResultExt; + use std::os::unix::prelude::*; + debug!("Setting executable permission on {}", path.as_ref().display()); + let metadata = path.as_ref().metadata()?; + let mut permissions = metadata.permissions(); + let mode = permissions.mode(); + let owner_can_execute = 0o0100; + permissions.set_mode(mode | owner_can_execute); + std::fs::set_permissions(path.as_ref(), permissions).anyhow_err() +} + +#[cfg(target_os = "windows")] +#[context("Failed to update permissions on `{}`", path.as_ref().display())] +pub fn allow_owner_execute(path: impl AsRef) -> Result { + // No-op on Windows. + Ok(()) +} + +pub fn check_if_identical(source: impl AsRef, target: impl AsRef) -> bool { + (|| -> Result { + #[allow(clippy::if_same_then_else)] // should be different after TODO + if metadata(&source)?.len() == metadata(&target)?.len() { + Ok(true) + } else if read(&source)? == read(&target)? { + // TODO: Not good for large files, should process them chunk by chunk. + Ok(true) + } else { + Ok(false) + } + })() + .unwrap_or(false) +} + +pub fn copy_file_if_different(source: impl AsRef, target: impl AsRef) -> Result { + if !check_if_identical(&source, &target) { + trace!( + "Modified, will copy {} to {}.", + source.as_ref().display(), + target.as_ref().display() + ); + copy(&source, &target)?; + } else { + trace!("No changes, skipping {}.", source.as_ref().display()) + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::log::setup_logging; + use ::tokio; + + #[tokio::test] + #[ignore] + async fn copy_if_different_test() -> Result { + setup_logging()?; + copy_if_different("../../..", r"C:\temp\out").await?; + Ok(()) + } +} diff --git a/build/base/src/fs/wrappers.rs b/build/base/src/fs/wrappers.rs new file mode 100644 index 000000000000..bccd902c8f6d --- /dev/null +++ b/build/base/src/fs/wrappers.rs @@ -0,0 +1,81 @@ +//! Wrappers over [`std::fs`] functions that provide sensible error messages, i.e. explaining what +//! operation was attempted and what was the relevant path. +//! +//! Unless there is a specific reason to use the standard library functions, you should use these. + +use crate::prelude::*; + +use std::fs::File; +use std::fs::Metadata; +use std::io::Write; + + + +// ============== +// === Export === +// ============== + +#[context("Failed to obtain metadata for file: {}", path.as_ref().display())] +pub fn metadata>(path: P) -> Result { + std::fs::metadata(&path).anyhow_err() +} + +#[context("Failed to copy file from {} to {}", from.as_ref().display(), to.as_ref().display())] +pub fn copy(from: impl AsRef, to: impl AsRef) -> Result { + std::fs::copy(&from, &to).anyhow_err() +} + +#[context("Failed to rename file from {} to {}", from.as_ref().display(), to.as_ref().display())] +pub fn rename(from: impl AsRef, to: impl AsRef) -> Result { + std::fs::rename(&from, &to).anyhow_err() +} + +#[context("Failed to read the file: {}", path.as_ref().display())] +pub fn read(path: impl AsRef) -> Result> { + std::fs::read(&path).anyhow_err() +} + +#[context("Failed to read the directory: {}", path.as_ref().display())] +pub fn read_dir(path: impl AsRef) -> Result { + std::fs::read_dir(&path).anyhow_err() +} + +#[context("Failed to read the file: {}", path.as_ref().display())] +pub fn read_to_string(path: impl AsRef) -> Result { + std::fs::read_to_string(&path).anyhow_err() +} + +#[context("Failed to write path: {}", path.as_ref().display())] +pub fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result { + std::fs::write(&path, contents).anyhow_err() +} + +pub fn append(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result { + std::fs::OpenOptions::new() + .append(true) + .create(true) + .open(&path) + .context(format!("Failed to open {} for writing.", path.as_ref().display()))? + .write_all(contents.as_ref()) + .context(format!("Failed to write to {}.", path.as_ref().display())) +} + +#[context("Failed to open path for reading: {}", path.as_ref().display())] +pub fn open(path: impl AsRef) -> Result { + File::open(&path).anyhow_err() +} + +#[context("Failed to open path for writing: {}", path.as_ref().display())] +pub fn create(path: impl AsRef) -> Result { + File::create(&path).anyhow_err() +} + +#[context("Failed to canonicalize path: {}", path.as_ref().display())] +pub fn canonicalize(path: impl AsRef) -> Result { + std::fs::canonicalize(&path).anyhow_err() +} + +#[context("Failed to create missing directories no path: {}", path.as_ref().display())] +pub fn create_dir_all(path: impl AsRef) -> Result { + std::fs::create_dir_all(&path).anyhow_err() +} diff --git a/build/base/src/lib.rs b/build/base/src/lib.rs new file mode 100644 index 000000000000..25eaa74eed35 --- /dev/null +++ b/build/base/src/lib.rs @@ -0,0 +1,99 @@ +// === Features === +#![feature(pin_macro)] +#![feature(default_free_fn)] +#![feature(result_flattening)] +#![feature(associated_type_bounds)] +#![feature(extend_one)] +// === Standard Linter Configuration === +#![deny(non_ascii_idents)] +#![warn(unsafe_code)] +#![allow(clippy::bool_to_int_with_if)] +#![allow(clippy::let_and_return)] + + +// ============== +// === Export === +// ============== + +pub mod extensions; +pub mod fs; + + + +pub mod prelude { + + pub type Result = anyhow::Result; + + pub use std::borrow::Borrow; + pub use std::borrow::BorrowMut; + pub use std::borrow::Cow; + pub use std::collections::BTreeMap; + pub use std::collections::BTreeSet; + pub use std::collections::HashMap; + pub use std::collections::HashSet; + pub use std::default::default; + pub use std::ffi::OsStr; + pub use std::ffi::OsString; + pub use std::fmt::Debug; + pub use std::fmt::Display; + pub use std::fmt::Formatter; + pub use std::future::ready; + pub use std::future::Future; + pub use std::hash::Hash; + pub use std::io::Read; + pub use std::io::Seek; + pub use std::iter::once; + pub use std::iter::FromIterator; + pub use std::marker::PhantomData; + pub use std::ops::Deref; + pub use std::ops::DerefMut; + pub use std::ops::Range; + pub use std::path::Path; + pub use std::path::PathBuf; + pub use std::pin::pin; + pub use std::pin::Pin; + pub use std::sync::Arc; + + pub use crate::extensions::from_string::FromString; + pub use crate::extensions::future::FutureExt as _; + pub use crate::extensions::future::TryFutureExt as _; + pub use crate::extensions::iterator::IteratorExt as _; + pub use crate::extensions::iterator::TryIteratorExt as _; + pub use crate::extensions::path::PathExt as _; + pub use crate::extensions::result::ResultExt as _; + pub use crate::extensions::str::StrLikeExt; + + pub use anyhow::anyhow; + pub use anyhow::bail; + pub use anyhow::ensure; + pub use anyhow::Context as _; + pub use fn_error_context::context; + pub use futures_util::future::BoxFuture; + pub use futures_util::select; + pub use futures_util::stream::BoxStream; + pub use futures_util::try_join; + pub use futures_util::AsyncWrite; + pub use futures_util::FutureExt as _; + pub use futures_util::Stream; + pub use futures_util::StreamExt as _; + pub use futures_util::TryFuture; + pub use futures_util::TryFutureExt as _; + pub use futures_util::TryStream; + pub use futures_util::TryStreamExt as _; + pub use serde::de::DeserializeOwned; + pub use serde::Deserialize; + pub use serde::Serialize; + pub use tracing::debug; + pub use tracing::debug_span; + pub use tracing::error; + pub use tracing::error_span; + pub use tracing::info; + pub use tracing::info_span; + pub use tracing::instrument; + pub use tracing::span; + pub use tracing::trace; + pub use tracing::trace_span; + pub use tracing::warn; + pub use tracing::warn_span; + pub use tracing::Instrument; +} diff --git a/build/build/src/engine/context.rs b/build/build/src/engine/context.rs index a7d3cd667986..429231c9bc35 100644 --- a/build/build/src/engine/context.rs +++ b/build/build/src/engine/context.rs @@ -32,6 +32,7 @@ use ide_ci::programs::Sbt; use sysinfo::SystemExt; + pub type FutureEnginePackage = BoxFuture<'static, Result>; pub type EnginePackageProvider = dyn FnMut() -> FutureEnginePackage + Send + Sync + 'static; diff --git a/build/build/src/env.rs b/build/build/src/env.rs index 8690a93f777a..5fd5621f537f 100644 --- a/build/build/src/env.rs +++ b/build/build/src/env.rs @@ -1,10 +1,11 @@ #[allow(unused_imports)] use crate::prelude::*; -use ide_ci::define_env_var; +use ide_ci::define_env_var; use ide_ci::programs::docker::ContainerId; + define_env_var! { ENSO_RELEASE_ID, octocrab::models::ReleaseId; diff --git a/build/build/src/release.rs b/build/build/src/release.rs index aa1190ba7a47..b971685fce4e 100644 --- a/build/build/src/release.rs +++ b/build/build/src/release.rs @@ -11,6 +11,8 @@ use ide_ci::programs::Docker; use octocrab::models::repos::Release; use tempfile::tempdir; + + pub async fn create_release(context: &BuildContext) -> Result { let versions = &context.triple.versions; let commit = ide_ci::actions::env::GITHUB_SHA.get()?; diff --git a/build/build/src/repo.rs b/build/build/src/repo.rs index 4618e607e978..3e69450792cd 100644 --- a/build/build/src/repo.rs +++ b/build/build/src/repo.rs @@ -1,7 +1,14 @@ use crate::prelude::*; + +// ============== +// === Export === +// ============== + pub mod cloud; + + /// Heuristic that checks if given path can be plausibly considered to be the root of the Enso /// repository. /// diff --git a/build/build/src/repo/cloud.rs b/build/build/src/repo/cloud.rs index 8063aab76121..afa3f38f2cc2 100644 --- a/build/build/src/repo/cloud.rs +++ b/build/build/src/repo/cloud.rs @@ -2,6 +2,8 @@ use crate::prelude::*; use ide_ci::github::RepoRef; + + pub const CLOUD_REPO: RepoRef = RepoRef { owner: "enso-org", name: "cloud-v2" }; pub const CLOUD_DEFAULT_BRANCH: &str = "main"; diff --git a/build/ci_utils/Cargo.toml b/build/ci_utils/Cargo.toml index a852960c20c7..9f718e9a1c40 100644 --- a/build/ci_utils/Cargo.toml +++ b/build/ci_utils/Cargo.toml @@ -21,10 +21,10 @@ data-encoding = "2.3.2" derivative = "2.2.0" derive_more = "0.99.17" dirs = "4.0.0" +enso-build-base = { path = "../base" } filetime = "0.2.15" flate2 = "1.0.22" flume = "0.10.10" -fn-error-context = "0.2.0" fs_extra = "1.2.0" futures = "0.3.17" futures-util = "0.3.17" @@ -60,7 +60,6 @@ regex = "1.5.4" reqwest = { version = "0.11.5", default-features = false, features = [ "stream" ] } -snafu = "0.7.0" semver = { version = "1.0.4", features = ["serde"] } serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0.68" diff --git a/build/ci_utils/src/anyhow.rs b/build/ci_utils/src/anyhow.rs deleted file mode 100644 index ff5d665a05c7..000000000000 --- a/build/ci_utils/src/anyhow.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::prelude::*; - -use anyhow::Error; - - - -pub trait ResultExt { - fn anyhow_err(self) -> Result; - - #[allow(clippy::type_complexity)] - fn flatten_fut( - self, - ) -> futures::future::Either< - std::future::Ready>, - futures::future::IntoFuture, - > - where T: TryFuture>; - - // fn flatten_fut(self) -> impl Future> - // where T: TryFuture> { - // async move { fut?.into_future().await } - // } - // fn flatten_fut(self) - // where T: TryFuture; -} - -impl ResultExt for std::result::Result -where E: Into -{ - fn anyhow_err(self) -> Result { - self.map_err(E::into) - } - - fn flatten_fut( - self, - ) -> futures::future::Either< - std::future::Ready>, - futures::future::IntoFuture, - > - where T: TryFuture> { - match self { - Ok(fut) => fut.into_future().right_future(), - Err(e) => ready(Err(T::Error::from(e))).left_future(), - } - } -} diff --git a/build/ci_utils/src/extensions.rs b/build/ci_utils/src/extensions.rs index 6360b7313cc2..60f627d24cd5 100644 --- a/build/ci_utils/src/extensions.rs +++ b/build/ci_utils/src/extensions.rs @@ -5,15 +5,8 @@ pub mod child; pub mod clap; pub mod command; -pub mod from_string; -pub mod future; -pub mod iterator; -pub mod maps; pub mod octocrab; pub mod os; pub mod output; -pub mod path; pub mod reqwest; -pub mod result; -pub mod str; pub mod version; diff --git a/build/ci_utils/src/fs.rs b/build/ci_utils/src/fs.rs index fd5828248b29..76959ed7cab2 100644 --- a/build/ci_utils/src/fs.rs +++ b/build/ci_utils/src/fs.rs @@ -3,7 +3,6 @@ use crate::prelude::*; use async_compression::tokio::bufread::GzipEncoder; use async_compression::Level; use fs_extra::dir::CopyOptions; -use std::fs::File; // ============== @@ -13,134 +12,14 @@ use std::fs::File; pub mod tokio; pub mod wrappers; -pub use wrappers::*; +pub use enso_build_base::fs::*; -///////////////////////////// - -/// Like the standard version but will create any missing parent directories from the path. -#[context("Failed to write path: {}", path.as_ref().display())] -pub fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result { - create_parent_dir_if_missing(&path)?; - wrappers::write(&path, &contents) -} - -/// Serialize the data to JSON text and write it to the file. -/// -/// See [`write`]. -#[context("Failed to write path: {}", path.as_ref().display())] -pub fn write_json(path: impl AsRef, contents: &impl Serialize) -> Result { - let contents = serde_json::to_string(contents)?; - write(&path, &contents) -} - -/// Like the standard version but will create any missing parent directories from the path. -#[context("Failed to open path for writing: {}", path.as_ref().display())] -pub fn create(path: impl AsRef) -> Result { - create_parent_dir_if_missing(&path)?; - wrappers::create(&path) -} - -/////////////////////////// - -#[context("Failed to read the file: {}", path.as_ref().display())] -pub fn read_string_into(path: impl AsRef) -> Result { - read_to_string(&path)?.parse2() -} - -/// Create a directory (and all missing parent directories), -/// -/// Does not fail when a directory already exists. -#[context("Failed to create directory {}", path.as_ref().display())] -pub fn create_dir_if_missing(path: impl AsRef) -> Result { - let result = std::fs::create_dir_all(&path); - match result { - Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()), - result => result.anyhow_err(), - } -} - -/// Create a parent directory for path (and all missing parent directories), -/// -/// Does not fail when a directory already exists. -#[context("Failed to create parent directory for {}", path.as_ref().display())] -pub fn create_parent_dir_if_missing(path: impl AsRef) -> Result { - if let Some(parent) = path.as_ref().parent() { - create_dir_if_missing(parent)?; - Ok(parent.into()) - } else { - bail!("No parent directory for path {}.", path.as_ref().display()) - } -} - -/// Remove a directory with all its subtree. -/// -/// Does not fail if the directory is not found. -#[tracing::instrument(fields(path = %path.as_ref().display()))] -#[context("Failed to remove directory {}", path.as_ref().display())] -pub fn remove_dir_if_exists(path: impl AsRef) -> Result { - let result = std::fs::remove_dir_all(&path); - match result { - Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), - result => result.anyhow_err(), - } -} - -/// Remove a regular file. -/// -/// Does not fail if the file is not found. -#[tracing::instrument(fields(path = %path.as_ref().display()))] -#[context("Failed to remove file {}", path.as_ref().display())] -pub fn remove_file_if_exists(path: impl AsRef) -> Result<()> { - let result = std::fs::remove_file(&path); - match result { - Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), - result => result.anyhow_err(), - } -} - -/// Remove a file being either directory or regular file.. -/// -/// Does not fail if the file is not found. -#[context("Failed to remove entry {} (if exists)", path.as_ref().display())] -pub fn remove_if_exists(path: impl AsRef) -> Result { - let path = path.as_ref(); - if path.is_dir() { - remove_dir_if_exists(path) - } else { - remove_file_if_exists(path) - } -} - -#[context("Failed to create symlink {} => {}", src.as_ref().display(), dst.as_ref().display())] -pub fn symlink_auto(src: impl AsRef, dst: impl AsRef) -> Result { - create_parent_dir_if_missing(&dst)?; - symlink::symlink_auto(&src, &dst).anyhow_err() -} - -/// Recreate directory, so it exists and is empty. -pub fn reset_dir(path: impl AsRef) -> Result { - let path = path.as_ref(); - debug!("Will reset directory {}", path.display()); - remove_dir_if_exists(path)?; - create_dir_if_missing(path)?; - Ok(()) -} - -pub fn require_exist(path: impl AsRef) -> Result { - if path.as_ref().exists() { - trace!("{} does exist.", path.as_ref().display()); - Ok(()) - } else { - bail!("{} does not exist.", path.as_ref().display()) - } -} - #[tracing::instrument(skip_all, fields( - src = %source_file.as_ref().display(), - dest = %dest_dir.as_ref().display()), - err)] +src = %source_file.as_ref().display(), +dest = %dest_dir.as_ref().display()), +err)] pub fn copy_to(source_file: impl AsRef, dest_dir: impl AsRef) -> Result { require_exist(&source_file)?; create_dir_if_missing(dest_dir.as_ref())?; @@ -153,9 +32,9 @@ pub fn copy_to(source_file: impl AsRef, dest_dir: impl AsRef) -> Res #[tracing::instrument(skip_all, fields( - src = %source_file.as_ref().display(), - dest = %destination_file.as_ref().display()), - err)] +src = %source_file.as_ref().display(), +dest = %destination_file.as_ref().display()), +err)] pub fn copy(source_file: impl AsRef, destination_file: impl AsRef) -> Result { let source_file = source_file.as_ref(); let destination_file = destination_file.as_ref(); @@ -168,7 +47,7 @@ pub fn copy(source_file: impl AsRef, destination_file: impl AsRef) - options.content_only = true; fs_extra::dir::copy(source_file, destination_file, &options)?; } else { - wrappers::copy(source_file, destination_file)?; + enso_build_base::fs::wrappers::copy(source_file, destination_file)?; } } else { bail!("Cannot copy to the root path: {}", destination_file.display()); @@ -176,9 +55,6 @@ pub fn copy(source_file: impl AsRef, destination_file: impl AsRef) - Ok(()) } -pub fn same_existing_path(source: impl AsRef, destination: impl AsRef) -> Result { - Ok(canonicalize(source)? == canonicalize(destination)?) -} pub async fn mirror_directory(source: impl AsRef, destination: impl AsRef) -> Result { create_dir_if_missing(destination.as_ref())?; @@ -195,47 +71,6 @@ pub async fn mirror_directory(source: impl AsRef, destination: impl AsRef< } } -#[context("Failed because the path does not point to a directory: {}", path.as_ref().display())] -pub fn expect_dir(path: impl AsRef) -> Result { - let filetype = metadata(&path)?.file_type(); - if filetype.is_dir() { - Ok(()) - } else { - bail!("File is not directory, its type is: {filetype:?}") - } -} - - -#[context("Failed because the path does not point to a regular file: {}", path.as_ref().display())] -pub fn expect_file(path: impl AsRef) -> Result { - let filetype = metadata(&path)?.file_type(); - if filetype.is_file() { - Ok(()) - } else { - bail!("File is not a regular file, its type is: {filetype:?}") - } -} - -#[cfg(not(target_os = "windows"))] -#[context("Failed to update permissions on `{}`", path.as_ref().display())] -pub fn allow_owner_execute(path: impl AsRef) -> Result { - use crate::anyhow::ResultExt; - use std::os::unix::prelude::*; - debug!("Setting executable permission on {}", path.as_ref().display()); - let metadata = path.as_ref().metadata()?; - let mut permissions = metadata.permissions(); - let mode = permissions.mode(); - let owner_can_execute = 0o0100; - permissions.set_mode(mode | owner_can_execute); - std::fs::set_permissions(path.as_ref(), permissions).anyhow_err() -} - -#[cfg(target_os = "windows")] -#[context("Failed to update permissions on `{}`", path.as_ref().display())] -pub fn allow_owner_execute(path: impl AsRef) -> Result { - // No-op on Windows. - Ok(()) -} /// Get the size of a file after gzip compression. pub async fn compressed_size(path: impl AsRef) -> Result { @@ -244,39 +79,10 @@ pub async fn compressed_size(path: impl AsRef) -> Result crate::io::read_length(encoded_stream).await.map(into) } -pub fn check_if_identical(source: impl AsRef, target: impl AsRef) -> bool { - (|| -> Result { - #[allow(clippy::if_same_then_else)] // should be different after TODO - if metadata(&source)?.len() == metadata(&target)?.len() { - Ok(true) - } else if read(&source)? == read(&target)? { - // TODO: Not good for large files, should process them chunk by chunk. - Ok(true) - } else { - Ok(false) - } - })() - .unwrap_or(false) -} - -pub fn copy_file_if_different(source: impl AsRef, target: impl AsRef) -> Result { - if !check_if_identical(&source, &target) { - trace!( - "Modified, will copy {} to {}.", - source.as_ref().display(), - target.as_ref().display() - ); - copy(&source, &target)?; - } else { - trace!("No changes, skipping {}.", source.as_ref().display()) - } - Ok(()) -} - #[tracing::instrument(skip_all, fields( - src = %source.as_ref().display(), - dest = %target.as_ref().display()), - err)] +src = %source.as_ref().display(), +dest = %target.as_ref().display()), +err)] pub async fn copy_if_different(source: impl AsRef, target: impl AsRef) -> Result { if tokio::metadata(&source).await?.is_file() { return copy_file_if_different(source, target); @@ -293,17 +99,8 @@ pub async fn copy_if_different(source: impl AsRef, target: impl AsRef Result { - setup_logging()?; - copy_if_different("../../..", r"C:\temp\out").await?; - Ok(()) - } +#[context("Failed to create symlink {} => {}", src.as_ref().display(), dst.as_ref().display())] +pub fn symlink_auto(src: impl AsRef, dst: impl AsRef) -> Result { + create_parent_dir_if_missing(&dst)?; + symlink::symlink_auto(&src, &dst).anyhow_err() } diff --git a/build/ci_utils/src/fs/wrappers.rs b/build/ci_utils/src/fs/wrappers.rs index 691cf41c2cd3..4512340b73bc 100644 --- a/build/ci_utils/src/fs/wrappers.rs +++ b/build/ci_utils/src/fs/wrappers.rs @@ -1,82 +1,5 @@ -//! Wrappers over [`std::fs`] functions that provide sensible error messages, i.e. explaining what -//! operation was attempted and what was the relevant path. - -use crate::prelude::*; - -use std::fs::File; -use std::fs::Metadata; -use std::io::Write; - - // ============== // === Export === // ============== pub mod tokio; - - - -#[context("Failed to obtain metadata for file: {}", path.as_ref().display())] -pub fn metadata>(path: P) -> Result { - std::fs::metadata(&path).anyhow_err() -} - -#[context("Failed to copy file from {} to {}", from.as_ref().display(), to.as_ref().display())] -pub fn copy(from: impl AsRef, to: impl AsRef) -> Result { - std::fs::copy(&from, &to).anyhow_err() -} - -#[context("Failed to rename file from {} to {}", from.as_ref().display(), to.as_ref().display())] -pub fn rename(from: impl AsRef, to: impl AsRef) -> Result { - std::fs::rename(&from, &to).anyhow_err() -} - -#[context("Failed to read the file: {}", path.as_ref().display())] -pub fn read(path: impl AsRef) -> Result> { - std::fs::read(&path).anyhow_err() -} - -#[context("Failed to read the directory: {}", path.as_ref().display())] -pub fn read_dir(path: impl AsRef) -> Result { - std::fs::read_dir(&path).anyhow_err() -} - -#[context("Failed to read the file: {}", path.as_ref().display())] -pub fn read_to_string(path: impl AsRef) -> Result { - std::fs::read_to_string(&path).anyhow_err() -} - -#[context("Failed to write path: {}", path.as_ref().display())] -pub fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result { - std::fs::write(&path, contents).anyhow_err() -} - -pub fn append(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result { - std::fs::OpenOptions::new() - .append(true) - .create(true) - .open(&path) - .context(format!("Failed to open {} for writing.", path.as_ref().display()))? - .write_all(contents.as_ref()) - .context(format!("Failed to write to {}.", path.as_ref().display())) -} - -#[context("Failed to open path for reading: {}", path.as_ref().display())] -pub fn open(path: impl AsRef) -> Result { - File::open(&path).anyhow_err() -} - -#[context("Failed to open path for writing: {}", path.as_ref().display())] -pub fn create(path: impl AsRef) -> Result { - File::create(&path).anyhow_err() -} - -#[context("Failed to canonicalize path: {}", path.as_ref().display())] -pub fn canonicalize(path: impl AsRef) -> Result { - std::fs::canonicalize(&path).anyhow_err() -} - -#[context("Failed to create missing directories no path: {}", path.as_ref().display())] -pub fn create_dir_all(path: impl AsRef) -> Result { - std::fs::create_dir_all(&path).anyhow_err() -} diff --git a/build/ci_utils/src/fs/wrappers/tokio.rs b/build/ci_utils/src/fs/wrappers/tokio.rs index b8163739e3ae..0f7fb00374d1 100644 --- a/build/ci_utils/src/fs/wrappers/tokio.rs +++ b/build/ci_utils/src/fs/wrappers/tokio.rs @@ -5,6 +5,7 @@ use tokio::io::AsyncReadExt; use tokio_util::io::ReaderStream; + pub fn metadata>(path: P) -> BoxFuture<'static, Result> { let path = path.as_ref().to_owned(); tokio::fs::metadata(path).anyhow_err().boxed() diff --git a/build/ci_utils/src/future.rs b/build/ci_utils/src/future.rs index cf5bc7d4f720..2bb6ae99c6e7 100644 --- a/build/ci_utils/src/future.rs +++ b/build/ci_utils/src/future.rs @@ -4,6 +4,13 @@ use futures_util::future::OptionFuture; +pub fn receiver_to_stream( + mut receiver: tokio::sync::mpsc::Receiver, +) -> impl Stream { + futures::stream::poll_fn(move |ctx| receiver.poll_recv(ctx)) +} + + #[derive(Copy, Clone, Debug)] pub enum AsyncPolicy { Sequential, diff --git a/build/ci_utils/src/github.rs b/build/ci_utils/src/github.rs index bb273712a978..87a88a5b27fc 100644 --- a/build/ci_utils/src/github.rs +++ b/build/ci_utils/src/github.rs @@ -17,6 +17,7 @@ use reqwest::Response; use serde_json::json; + const MAX_PER_PAGE: u8 = 100; pub mod model; diff --git a/build/ci_utils/src/github/release.rs b/build/ci_utils/src/github/release.rs index d9fa28318edf..52fc49c8e229 100644 --- a/build/ci_utils/src/github/release.rs +++ b/build/ci_utils/src/github/release.rs @@ -1,12 +1,15 @@ use crate::prelude::*; -use mime::Mime; use crate::github::Repo; + +use mime::Mime; use octocrab::models::repos::Asset; use octocrab::models::ReleaseId; use reqwest::Body; use tracing::instrument; + + /// Types that uniquely identify a release and can be used to fetch it from GitHub. pub trait IsRelease: Debug { /// The release ID. diff --git a/build/ci_utils/src/github/repo.rs b/build/ci_utils/src/github/repo.rs index 47510e29d99f..337f9e49b6ee 100644 --- a/build/ci_utils/src/github/repo.rs +++ b/build/ci_utils/src/github/repo.rs @@ -1,5 +1,7 @@ use crate::prelude::*; + + /// Data denoting a specific GitHub repository. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, derive_more::Display)] #[display(fmt = "{}/{}", owner, name)] diff --git a/build/ci_utils/src/lib.rs b/build/ci_utils/src/lib.rs index 56cd564b973d..96e7f256778c 100644 --- a/build/ci_utils/src/lib.rs +++ b/build/ci_utils/src/lib.rs @@ -41,7 +41,6 @@ // ============== pub mod actions; -pub mod anyhow; pub mod archive; pub mod buffer; pub mod cache; @@ -71,29 +70,12 @@ pub mod serde; pub mod prelude { + pub use enso_build_base::prelude::*; - pub type Result = anyhow::Result; - pub use anyhow::anyhow; - pub use anyhow::bail; - pub use anyhow::ensure; - pub use anyhow::Context as _; pub use async_trait::async_trait; pub use bytes::Bytes; pub use derivative::Derivative; pub use derive_more::Display; - pub use fn_error_context::context; - pub use futures_util::future::BoxFuture; - pub use futures_util::select; - pub use futures_util::stream::BoxStream; - pub use futures_util::try_join; - pub use futures_util::AsyncWrite; - pub use futures_util::FutureExt as _; - pub use futures_util::Stream; - pub use futures_util::StreamExt as _; - pub use futures_util::TryFuture; - pub use futures_util::TryFutureExt as _; - pub use futures_util::TryStream; - pub use futures_util::TryStreamExt as _; pub use ifmt::iformat; pub use itertools::Itertools; pub use lazy_static::lazy_static; @@ -102,62 +84,15 @@ pub mod prelude { pub use platforms::target::Arch; pub use platforms::target::OS; pub use semver::Version; - pub use serde::de::DeserializeOwned; - pub use serde::Deserialize; - pub use serde::Serialize; pub use shrinkwraprs::Shrinkwrap; - pub use snafu::Snafu; - pub use std::borrow::Borrow; - pub use std::borrow::BorrowMut; - pub use std::borrow::Cow; - pub use std::collections::BTreeMap; - pub use std::collections::BTreeSet; - pub use std::collections::HashMap; - pub use std::collections::HashSet; - pub use std::default::default; - pub use std::ffi::OsStr; - pub use std::ffi::OsString; - pub use std::fmt::Debug; - pub use std::fmt::Display; - pub use std::fmt::Formatter; - pub use std::future::ready; - pub use std::future::Future; - pub use std::hash::Hash; - pub use std::io::Read; - pub use std::io::Seek; - pub use std::iter::once; - pub use std::iter::FromIterator; - pub use std::marker::PhantomData; - pub use std::ops::Deref; - pub use std::ops::DerefMut; - pub use std::ops::Range; - pub use std::path::Path; - pub use std::path::PathBuf; - pub use std::pin::pin; - pub use std::pin::Pin; - pub use std::sync::Arc; pub use tokio::io::AsyncWriteExt as _; - pub use tracing::debug; - pub use tracing::debug_span; - pub use tracing::error; - pub use tracing::error_span; - pub use tracing::info; - pub use tracing::info_span; - pub use tracing::instrument; - pub use tracing::span; - pub use tracing::trace; - pub use tracing::trace_span; - pub use tracing::warn; - pub use tracing::warn_span; - pub use tracing::Instrument; pub use url::Url; pub use uuid::Uuid; pub use crate::EMPTY_REQUEST_BODY; - pub use crate::anyhow::ResultExt; pub use crate::env::Variable as EnvironmentVariable; - pub use crate::extensions::str::StrLikeExt; + pub use crate::extensions::output::OutputExt as _; pub use crate::github::release::IsRelease; pub use crate::github::IsRepo; pub use crate::goodie::Goodie; @@ -177,14 +112,6 @@ pub mod prelude { pub use crate::env::new::TypedVariable as _; pub use crate::extensions::clap::ArgExt as _; pub use crate::extensions::command::CommandExt as _; - pub use crate::extensions::from_string::FromString; - pub use crate::extensions::future::FutureExt as _; - pub use crate::extensions::future::TryFutureExt as _; - pub use crate::extensions::iterator::IteratorExt; - pub use crate::extensions::iterator::TryIteratorExt; - pub use crate::extensions::output::OutputExt as _; - pub use crate::extensions::path::PathExt as _; - pub use crate::extensions::result::ResultExt as _; pub use crate::github::release::IsReleaseExt as _; pub use crate::program::command::provider::CommandProviderExt as _; pub use crate::program::version::IsVersion as _; diff --git a/build/ci_utils/src/models/config.rs b/build/ci_utils/src/models/config.rs index bbf614c2c2c0..e01984a1061d 100644 --- a/build/ci_utils/src/models/config.rs +++ b/build/ci_utils/src/models/config.rs @@ -4,14 +4,15 @@ use crate::prelude::*; use crate::github::IsRepo; use crate::github::OrganizationPointer; +use crate::github::Repo; use crate::serde::regex_vec; use crate::serde::single_or_sequence; -use crate::github::Repo; use regex::Regex; use std::collections::HashMap; + pub type Config = BTreeMap; /// Root type of the configuration file. pub type MachineConfig = Vec; diff --git a/build/ci_utils/src/programs/seven_zip.rs b/build/ci_utils/src/programs/seven_zip.rs index f8bc4cf3dd44..5d481afd906c 100644 --- a/build/ci_utils/src/programs/seven_zip.rs +++ b/build/ci_utils/src/programs/seven_zip.rs @@ -1,7 +1,5 @@ use crate::prelude::*; -use snafu::Snafu; - #[derive(Clone, Copy, Debug)] @@ -26,48 +24,29 @@ impl Program for SevenZip { vec![] } - fn handle_exit_status(status: std::process::ExitStatus) -> anyhow::Result<()> { + fn handle_exit_status(status: std::process::ExitStatus) -> Result { if status.success() { Ok(()) } else if let Some(code) = status.code() { - Err(ExecutionError::from_exit_code(code).into()) + error_from_exit_code(code) } else { - Err(ExecutionError::Unknown.into()) + bail!("Unknown execution error.") } } } -// Cf https://7zip.bugaco.com/7zip/MANUAL/cmdline/exit_codes.htm -#[derive(Snafu, Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] -pub enum ExecutionError { - #[snafu(display( - "Warning (Non fatal error(s)). For example, one or more files were locked by some \ - other application, so they were not compressed." - ))] - Warning, - #[snafu(display("Fatal error"))] - Fatal, - #[snafu(display("Command line error"))] - CommandLine, - #[snafu(display("Not enough memory for operation"))] - NotEnoughMemory, - #[snafu(display("User stopped the process"))] - UserStopped, - #[snafu(display("Unrecognized error code"))] - Unknown, -} - -impl ExecutionError { - fn from_exit_code(code: i32) -> Self { - match code { - 1 => Self::Warning, - 2 => Self::Fatal, - 7 => Self::CommandLine, - 8 => Self::NotEnoughMemory, - 255 => Self::UserStopped, - _ => Self::Unknown, - } - } +pub fn error_from_exit_code(code: i32) -> anyhow::Result<()> { + let message = match code { + 1 => + "Warning (Non fatal error(s)). For example, one or more files were locked by some \ + other application, so they were not compressed.", + 2 => "Fatal error.", + 7 => "Command line error.", + 8 => "Not enough memory for operation.", + 255 => "User stopped the process.", + _ => "Unrecognized error code.", + }; + bail!(message); } impl SevenZip { diff --git a/build/ci_utils/src/programs/vswhere.rs b/build/ci_utils/src/programs/vswhere.rs index 865b1e527dc3..1337af83bc06 100644 --- a/build/ci_utils/src/programs/vswhere.rs +++ b/build/ci_utils/src/programs/vswhere.rs @@ -39,7 +39,9 @@ impl VsWhere { let stdout = command.run_stdout().await?; let instances = serde_json::from_str::>(&stdout)?; - Ok(instances.into_iter().next().ok_or(NoMsvcInstallation)?) + Ok(instances.into_iter().next().with_context(|| { + format!("No Visual Studio installation found with component {}.", component) + })?) } /// Looks up installation of Visual Studio that has installed @@ -54,10 +56,6 @@ impl VsWhere { } } -#[derive(Clone, Copy, Debug, Snafu)] -#[snafu(display("failed to find a MSVC installation"))] -pub struct NoMsvcInstallation; - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct InstanceInfo { @@ -139,7 +137,7 @@ impl From<&Format> for OsString { } // cf. https://docs.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-community?view=vs-2019&preserve-view=true -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Display)] pub enum Component { /// MSVC v142 - VS 2019 C++ x64/x86 build tools CppBuildTools, diff --git a/build/cli/Cargo.toml b/build/cli/Cargo.toml index b3782c40c64f..9a89b1d4c180 100644 --- a/build/cli/Cargo.toml +++ b/build/cli/Cargo.toml @@ -10,6 +10,7 @@ byte-unit = { version = "4.0.14", features = ["serde"] } clap = { version = "3.1.5", features = ["derive", "env", "wrap_help"] } chrono = "0.4.19" derivative = "2.2.0" +enso-build-base = { path = "../base" } enso-build = { path = "../build" } enso-formatter = { path = "../enso-formatter" } futures = "0.3.17" diff --git a/build/cli/src/arg.rs b/build/cli/src/arg.rs index 1ffe2d55202c..65af191ecc82 100644 --- a/build/cli/src/arg.rs +++ b/build/cli/src/arg.rs @@ -6,8 +6,8 @@ use clap::Args; use clap::Parser; use clap::Subcommand; use derivative::Derivative; +use enso_build_base::extensions::path::display_fmt; use ide_ci::cache; -use ide_ci::extensions::path::display_fmt; use ide_ci::github::Repo; use octocrab::models::RunId; diff --git a/build/cli/src/arg/ide.rs b/build/cli/src/arg/ide.rs index 98be043d6c58..7ca6325c5bdd 100644 --- a/build/cli/src/arg/ide.rs +++ b/build/cli/src/arg/ide.rs @@ -12,6 +12,8 @@ use enso_build::project::gui::Gui; use enso_build::project::wasm::DEFAULT_INTEGRATION_TESTS_WASM_TIMEOUT; use octocrab::models::ReleaseId; + + source_args_hlp!(Target, "ide", BuildInput); #[derive(Args, Clone, Debug, PartialEq)]