mirror of
https://github.com/Zenithsiz/zbuild.git
synced 2026-02-04 06:28:57 +00:00
Replaced std::fs usages with tokio::fs where possible.
This commit is contained in:
parent
5a010019b5
commit
d8ab30c7ae
73
src/build.rs
73
src/build.rs
@ -25,12 +25,13 @@ use {
|
||||
futures::{stream::FuturesUnordered, StreamExt, TryStreamExt},
|
||||
std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
mem,
|
||||
path::Path,
|
||||
sync::Arc,
|
||||
time::{Duration, SystemTime},
|
||||
},
|
||||
tokio::{
|
||||
fs,
|
||||
process::Command,
|
||||
sync::{OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock, Semaphore},
|
||||
},
|
||||
@ -43,9 +44,6 @@ use {
|
||||
// when a single rule has multiple targets that each want to be
|
||||
// built simultaneously
|
||||
|
||||
// TODO: `fs::metadata` isn't async-aware, so it might slow us down a lot
|
||||
// when every target is up to date, so see alternatives
|
||||
|
||||
/// Event
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
@ -106,7 +104,7 @@ impl Builder {
|
||||
|
||||
/// Returns all targets built.
|
||||
///
|
||||
/// If any target is still being build, ignores it.
|
||||
/// Waits for targets being built
|
||||
pub async fn targets(&self) -> HashMap<Target<String>, Result<BuildResult, AppError>> {
|
||||
self.targets
|
||||
.iter()
|
||||
@ -214,7 +212,7 @@ impl Builder {
|
||||
// Else, if no rule can make it, we'll assume it's an existing file
|
||||
// and return it's modification date as the build time.
|
||||
None => {
|
||||
let metadata = fs::metadata(file).map_err(AppError::missing_file(file))?;
|
||||
let metadata = fs::metadata(file).await.map_err(AppError::missing_file(file))?;
|
||||
let build_time = self::file_modified_time(metadata);
|
||||
tracing::trace!(?target, ?build_time, "Found target file");
|
||||
let res = BuildResult {
|
||||
@ -261,41 +259,45 @@ impl Builder {
|
||||
let normal_deps = rule
|
||||
.deps
|
||||
.iter()
|
||||
.map(|dep| match *dep {
|
||||
.map(async move |dep| match *dep {
|
||||
DepItem::File { ref file, is_static } => Ok(Dep::File {
|
||||
file,
|
||||
is_static,
|
||||
is_dep_file: false,
|
||||
is_output: false,
|
||||
exists: fs::try_exists(file).map_err(AppError::check_file_exists(file))?,
|
||||
exists: fs_try_exists(file).await.map_err(AppError::check_file_exists(file))?,
|
||||
}),
|
||||
DepItem::DepsFile { ref file, is_static } => Ok(Dep::File {
|
||||
file,
|
||||
is_static,
|
||||
is_dep_file: true,
|
||||
is_output: false,
|
||||
exists: fs::try_exists(file).map_err(AppError::check_file_exists(file))?,
|
||||
exists: fs_try_exists(file).await.map_err(AppError::check_file_exists(file))?,
|
||||
}),
|
||||
DepItem::Rule { ref name, ref pats } => Ok(Dep::Rule { name, pats }),
|
||||
})
|
||||
.collect::<Result<Vec<_>, AppError>>()?;
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
|
||||
// And all output dependencies
|
||||
let out_deps = rule
|
||||
.output
|
||||
.iter()
|
||||
.map(|out| match out {
|
||||
.map(async move |out| match out {
|
||||
OutItem::File { .. } => Ok(None),
|
||||
OutItem::DepsFile { file } => Ok(Some(Dep::File {
|
||||
file,
|
||||
is_static: false,
|
||||
is_dep_file: true,
|
||||
is_output: true,
|
||||
exists: fs::try_exists(file).map_err(AppError::check_file_exists(file))?,
|
||||
exists: fs_try_exists(file).await.map_err(AppError::check_file_exists(file))?,
|
||||
})),
|
||||
})
|
||||
.filter_map(|res| res.transpose())
|
||||
.collect::<Result<Vec<_>, AppError>>()?;
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.filter_map(async move |res| res.transpose())
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
|
||||
// Then build all dependencies, as well as any dependency files
|
||||
let deps = util::chain!(normal_deps, out_deps)
|
||||
@ -397,7 +399,7 @@ impl Builder {
|
||||
let deps = util::chain!(dep_res.zip(dep_guard), dep_deps.into_iter()).collect::<Vec<_>>();
|
||||
tracing::trace!(?target, ?rule.name, ?dep, ?deps, "Built target rule dependency dependencies");
|
||||
|
||||
Ok::<_, AppError>(deps)
|
||||
Ok(deps)
|
||||
}
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
@ -416,7 +418,7 @@ impl Builder {
|
||||
|
||||
// Afterwards check the last time we've built the rule and compare it with
|
||||
// the dependency build times.
|
||||
let rule_last_build_time = self::rule_last_build_time(&rule);
|
||||
let rule_last_build_time = self::rule_last_build_time(&rule).await;
|
||||
let needs_rebuilt = match (deps_last_build_time, &rule_last_build_time) {
|
||||
// If any files were missing, or we had no outputs, build
|
||||
(_, Err(_) | Ok(None)) => true,
|
||||
@ -440,7 +442,7 @@ impl Builder {
|
||||
|
||||
// Then get the build time
|
||||
// Note: If we don't have any outputs, just use the current time as the build time
|
||||
let cur_build_time = self::rule_last_build_time(&rule)?.unwrap_or_else(SystemTime::now);
|
||||
let cur_build_time = self::rule_last_build_time(&rule).await?.unwrap_or_else(SystemTime::now);
|
||||
let res = BuildResult {
|
||||
build_time: cur_build_time,
|
||||
built: needs_rebuilt,
|
||||
@ -461,7 +463,7 @@ impl Builder {
|
||||
rules: &Rules,
|
||||
) -> Result<Vec<(BuildResult, BuildLockDepGuard)>, AppError> {
|
||||
tracing::trace!(target=?parent_target, ?rule.name, ?dep_file, "Building dependencies of target rule dependency-file");
|
||||
let (output, deps) = self::parse_deps_file(dep_file)?;
|
||||
let (output, deps) = self::parse_deps_file(dep_file).await?;
|
||||
|
||||
match rule.output.is_empty() {
|
||||
// If there were no outputs, make sure it matches the rule name
|
||||
@ -508,7 +510,7 @@ impl Builder {
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok::<_, AppError>((res, dep_guard))
|
||||
Ok((res, dep_guard))
|
||||
}
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
@ -590,7 +592,7 @@ impl BuildLock {
|
||||
|
||||
/// Retrieves the result of this state.
|
||||
///
|
||||
/// Waits for any builders too finish
|
||||
/// Waits for any builders to finish
|
||||
pub async fn res(&self) -> Option<Result<BuildResult, AppError>> {
|
||||
self.state
|
||||
.read()
|
||||
@ -670,9 +672,9 @@ impl BuildResult {
|
||||
|
||||
/// Parses a dependencies file
|
||||
// TODO: Support multiple dependencies in each file
|
||||
fn parse_deps_file(file: &str) -> Result<(String, Vec<String>), AppError> {
|
||||
async fn parse_deps_file(file: &str) -> Result<(String, Vec<String>), AppError> {
|
||||
// Read it
|
||||
let contents = fs::read_to_string(file).map_err(AppError::read_file(file))?;
|
||||
let contents = fs::read_to_string(file).await.map_err(AppError::read_file(file))?;
|
||||
|
||||
// Parse it
|
||||
let (output, deps) = contents.split_once(':').ok_or_else(|| AppError::DepFileMissingColon {
|
||||
@ -688,26 +690,32 @@ fn parse_deps_file(file: &str) -> Result<(String, Vec<String>), AppError> {
|
||||
///
|
||||
/// Returns `Err` if any files didn't exist,
|
||||
/// Returns `Ok(None)` if rule has no outputs
|
||||
fn rule_last_build_time(rule: &Rule<String>) -> Result<Option<SystemTime>, AppError> {
|
||||
async fn rule_last_build_time(rule: &Rule<String>) -> Result<Option<SystemTime>, AppError> {
|
||||
// Note: We get the time of the oldest file in order to ensure all
|
||||
// files are at-least that old
|
||||
rule.output
|
||||
let built_time = rule
|
||||
.output
|
||||
.iter()
|
||||
.map(|item| {
|
||||
.map(async move |item| {
|
||||
let file = match item {
|
||||
OutItem::File { file } => file,
|
||||
OutItem::DepsFile { file } => file,
|
||||
};
|
||||
fs::metadata(file)
|
||||
.await
|
||||
.map(self::file_modified_time)
|
||||
.map_err(AppError::read_file_metadata(file))
|
||||
})
|
||||
.reduce(|lhs, rhs| Ok(lhs?.min(rhs?)))
|
||||
.transpose()
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?
|
||||
.into_iter()
|
||||
.min();
|
||||
Ok(built_time)
|
||||
}
|
||||
|
||||
/// Returns the file modified time
|
||||
fn file_modified_time(metadata: fs::Metadata) -> SystemTime {
|
||||
fn file_modified_time(metadata: std::fs::Metadata) -> SystemTime {
|
||||
let file_time = FileTime::from_last_modification_time(&metadata);
|
||||
let unix_offset = Duration::new(file_time.unix_seconds() as u64, file_time.nanoseconds());
|
||||
|
||||
@ -742,3 +750,12 @@ pub fn find_rule_for_file(file: &str, rules: &Rules) -> Result<Option<Rule<Strin
|
||||
// If we got here, there was no matching rule
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Async `std::fs_try_exists`
|
||||
async fn fs_try_exists(path: impl AsRef<Path>) -> Result<bool, std::io::Error> {
|
||||
match fs::metadata(path).await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ pub enum AppError {
|
||||
/// Other
|
||||
// TODO: Removes usages of this, it's for quick prototyping
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error),
|
||||
Other(anyhow::Error),
|
||||
|
||||
/// Get current directory
|
||||
#[error("Unable to get current directory")]
|
||||
|
||||
@ -56,7 +56,8 @@ impl Watcher {
|
||||
tracing::warn!("Error while watching: {:?}", anyhow::Error::from(err))
|
||||
},
|
||||
})
|
||||
.context("Unable to create file watcher")?;
|
||||
.context("Unable to create file watcher")
|
||||
.map_err(AppError::Other)?;
|
||||
|
||||
// Then create the task to register all dependencies
|
||||
// TODO: Not do this?
|
||||
@ -121,7 +122,8 @@ impl Watcher {
|
||||
self.watcher
|
||||
.watcher()
|
||||
.watch(path, notify::RecursiveMode::NonRecursive)
|
||||
.with_context(|| format!("Unable to watch path {path:?}"))?;
|
||||
.with_context(|| format!("Unable to watch path {path:?}"))
|
||||
.map_err(AppError::Other)?;
|
||||
}
|
||||
|
||||
let rev_deps = &self.rev_deps;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user