mirror of
https://github.com/Zenithsiz/zbuild.git
synced 2026-02-03 22:23:53 +00:00
Each build is now performed in it's own tokio task.
This implements proper multithreading to the program.
This commit is contained in:
parent
6c1fa7c056
commit
f10391dc08
@ -24,7 +24,7 @@ pin-project = "1.1.5"
|
||||
serde = { version = "1.0.205", features = ["derive"] }
|
||||
serde_yaml = "0.9.34"
|
||||
smallvec = { version = "1.13.2", features = ["may_dangle"] }
|
||||
tokio = { version = "1.39.2", features = ["full"] }
|
||||
tokio = { version = "1.39.2", features = ["full", "tracing"] }
|
||||
tokio-stream = "0.1.15"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
|
||||
73
src/build.rs
73
src/build.rs
@ -22,12 +22,13 @@ use {
|
||||
Expander,
|
||||
Rules,
|
||||
},
|
||||
anyhow::Context,
|
||||
dashmap::DashMap,
|
||||
futures::{stream::FuturesUnordered, StreamExt, TryFutureExt},
|
||||
indexmap::IndexMap,
|
||||
itertools::Itertools,
|
||||
std::{borrow::Cow, collections::BTreeMap, ffi::OsStr, ops::Try, sync::Arc, time::SystemTime},
|
||||
tokio::{fs, process, sync::Semaphore},
|
||||
std::{borrow::Cow, collections::BTreeMap, ffi::OsStr, future::Future, ops::Try, sync::Arc, time::SystemTime},
|
||||
tokio::{fs, process, sync::Semaphore, task},
|
||||
};
|
||||
|
||||
/// Event
|
||||
@ -189,9 +190,9 @@ impl Builder {
|
||||
|
||||
/// Builds an expression-encoded target
|
||||
pub async fn build_expr(
|
||||
&self,
|
||||
self: &Arc<Self>,
|
||||
target: &Target<Expr>,
|
||||
rules: &Rules,
|
||||
rules: &Arc<Rules>,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason,
|
||||
) -> Result<(BuildResult, Option<BuildLockDepGuard>), AppError> {
|
||||
@ -207,10 +208,28 @@ impl Builder {
|
||||
}
|
||||
|
||||
/// Builds a target
|
||||
pub async fn build(
|
||||
&self,
|
||||
target: &Target<ArcStr>,
|
||||
rules: &Rules,
|
||||
// TODO: Remove this wrapper function once the compiler behaves.
|
||||
#[expect(
|
||||
clippy::manual_async_fn,
|
||||
reason = "For some reason, without this wrapper, the compiler
|
||||
can't see that `build_inner`'s future is `Send`"
|
||||
)]
|
||||
pub fn build<'a>(
|
||||
self: &'a Arc<Self>,
|
||||
target: &'a Target<ArcStr>,
|
||||
rules: &'a Arc<Rules>,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason,
|
||||
) -> impl Future<Output = Result<(BuildResult, Option<BuildLockDepGuard>), AppError>> + Send + 'a {
|
||||
async move { self.build_inner(target, rules, ignore_missing, reason).await }
|
||||
}
|
||||
|
||||
/// Inner function for [`Builder::build`]
|
||||
#[expect(clippy::too_many_lines, reason = "TODO: Split it up more")]
|
||||
pub async fn build_inner<'a>(
|
||||
self: &'a Arc<Self>,
|
||||
target: &'a Target<ArcStr>,
|
||||
rules: &'a Arc<Rules>,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason,
|
||||
) -> Result<(BuildResult, Option<BuildLockDepGuard>), AppError> {
|
||||
@ -224,9 +243,10 @@ impl Builder {
|
||||
},
|
||||
ref target @ Target::Rule { .. } => target.clone(),
|
||||
};
|
||||
let target = Arc::new(target);
|
||||
|
||||
// Check if we're being built recursively, and if so, return error
|
||||
reason.for_each(|parent_target| match &target == parent_target {
|
||||
reason.for_each(|parent_target| match &*target == parent_target {
|
||||
true => Err(AppError::FoundRecursiveRule {
|
||||
target: target.to_string(),
|
||||
parent_targets: reason.collect_all().iter().map(Target::to_string).collect(),
|
||||
@ -236,7 +256,7 @@ impl Builder {
|
||||
|
||||
// Get the rule for the target
|
||||
let Some((rule, target_rule)) = self.target_rule(&target, rules)? else {
|
||||
match target {
|
||||
match *target {
|
||||
Target::File { ref file, .. } => match fs::symlink_metadata(&**file).await {
|
||||
Ok(metadata) => {
|
||||
let build_time = metadata.modified().map_err(AppError::get_file_modified_time(&**file))?;
|
||||
@ -262,10 +282,10 @@ impl Builder {
|
||||
));
|
||||
},
|
||||
Err(err) =>
|
||||
do yeet AppError::MissingFile {
|
||||
return Err(AppError::MissingFile {
|
||||
file_path: (**file).into(),
|
||||
source: err,
|
||||
},
|
||||
}),
|
||||
},
|
||||
// Note: If `target_rule` returns `Err` if this was a rule, so we can never reach here
|
||||
Target::Rule { .. } => unreachable!(),
|
||||
@ -306,11 +326,24 @@ impl Builder {
|
||||
}
|
||||
};
|
||||
|
||||
// Else build
|
||||
match self
|
||||
.build_unchecked(&target, &rule, rules, ignore_missing, reason)
|
||||
let res = task::Builder::new()
|
||||
.name(&format!("Build {target}"))
|
||||
.spawn({
|
||||
let this = Arc::clone(self);
|
||||
let target = Arc::clone(&target);
|
||||
let rules = Arc::clone(rules);
|
||||
async move {
|
||||
this.build_unchecked(&target, &rule, &rules, ignore_missing, reason)
|
||||
.await
|
||||
}
|
||||
})
|
||||
.context("Unable to execute task")
|
||||
.map_err(AppError::Other)?
|
||||
.await
|
||||
{
|
||||
.context("Unable to join task")
|
||||
.map_err(AppError::Other)?;
|
||||
|
||||
match res {
|
||||
Ok(res) => {
|
||||
build_guard.finish(res);
|
||||
|
||||
@ -335,10 +368,10 @@ impl Builder {
|
||||
/// Builds a target without checking if the target is already being built.
|
||||
#[expect(clippy::too_many_lines, reason = "TODO: Split this function onto smaller ones")]
|
||||
async fn build_unchecked(
|
||||
&self,
|
||||
self: &Arc<Self>,
|
||||
target: &Target<ArcStr>,
|
||||
rule: &Rule<ArcStr>,
|
||||
rules: &Rules,
|
||||
rules: &Arc<Rules>,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason,
|
||||
) -> Result<BuildResult, AppError> {
|
||||
@ -578,11 +611,11 @@ impl Builder {
|
||||
///
|
||||
/// Returns the latest modification date of the dependencies
|
||||
async fn build_deps_file(
|
||||
&self,
|
||||
self: &Arc<Self>,
|
||||
parent_target: &Target<ArcStr>,
|
||||
deps_file: &str,
|
||||
rule: &Rule<ArcStr>,
|
||||
rules: &Rules,
|
||||
rules: &Arc<Rules>,
|
||||
ignore_missing: bool,
|
||||
reason: &BuildReason,
|
||||
) -> Result<Vec<(Target<ArcStr>, BuildResult, Option<BuildLockDepGuard>)>, AppError> {
|
||||
|
||||
20
src/main.rs
20
src/main.rs
@ -63,7 +63,7 @@ use {
|
||||
watcher::Watcher,
|
||||
};
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
#[tokio::main]
|
||||
#[expect(clippy::too_many_lines, reason = "TODO: Split it up more")]
|
||||
async fn main() -> ExitResult {
|
||||
// Get all args
|
||||
@ -95,6 +95,7 @@ async fn main() -> ExitResult {
|
||||
|
||||
// Build the rules
|
||||
let rules = Rules::new(&zbuild_file, ast);
|
||||
let rules = Arc::new(rules);
|
||||
tracing::trace!(?rules, "Built rules");
|
||||
|
||||
// Get the max number of jobs we can execute at once
|
||||
@ -149,6 +150,7 @@ async fn main() -> ExitResult {
|
||||
// Create the builder
|
||||
// Note: We should stop builds on the first error if we're *not* watching.
|
||||
let builder = Builder::new(jobs, !args.watch);
|
||||
let builder = Arc::new(builder);
|
||||
|
||||
// Then create the watcher, if we're watching
|
||||
let watcher = args
|
||||
@ -232,9 +234,9 @@ async fn find_zbuild() -> Result<PathBuf, AppError> {
|
||||
|
||||
/// Builds a target.
|
||||
async fn build_target<T: BuildableTargetInner + fmt::Display + fmt::Debug>(
|
||||
builder: &Builder,
|
||||
builder: &Arc<Builder>,
|
||||
target: &rules::Target<T>,
|
||||
rules: &Rules,
|
||||
rules: &Arc<Rules>,
|
||||
ignore_missing: bool,
|
||||
) -> Result<(), AppError> {
|
||||
tracing::debug!(%target, "Building target");
|
||||
@ -270,8 +272,8 @@ trait BuildableTargetInner: Sized {
|
||||
/// Builds this target
|
||||
async fn build(
|
||||
target: &rules::Target<Self>,
|
||||
builder: &Builder,
|
||||
rules: &Rules,
|
||||
builder: &Arc<Builder>,
|
||||
rules: &Arc<Rules>,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason,
|
||||
) -> Result<build::BuildResult, AppError>;
|
||||
@ -280,8 +282,8 @@ trait BuildableTargetInner: Sized {
|
||||
impl BuildableTargetInner for rules::Expr {
|
||||
async fn build(
|
||||
target: &rules::Target<Self>,
|
||||
builder: &Builder,
|
||||
rules: &Rules,
|
||||
builder: &Arc<Builder>,
|
||||
rules: &Arc<Rules>,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason,
|
||||
) -> Result<build::BuildResult, AppError> {
|
||||
@ -295,8 +297,8 @@ impl BuildableTargetInner for rules::Expr {
|
||||
impl BuildableTargetInner for ArcStr {
|
||||
async fn build(
|
||||
target: &rules::Target<Self>,
|
||||
builder: &Builder,
|
||||
rules: &Rules,
|
||||
builder: &Arc<Builder>,
|
||||
rules: &Arc<Rules>,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason,
|
||||
) -> Result<build::BuildResult, AppError> {
|
||||
|
||||
@ -87,7 +87,7 @@ impl Watcher {
|
||||
|
||||
/// Watches over all files and rebuilds any changed files
|
||||
#[expect(clippy::too_many_lines, reason = "TODO: Refactor")]
|
||||
pub async fn watch_rebuild(mut self, builder: &Builder, rules: &Rules, ignore_missing: bool) {
|
||||
pub async fn watch_rebuild(mut self, builder: &Arc<Builder>, rules: &Arc<Rules>, ignore_missing: bool) {
|
||||
let rev_deps = &self.rev_deps;
|
||||
futures::future::join(
|
||||
async move {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user