Skip to content

Commit

Permalink
Track outdatedness of explicitly defined global variables
Browse files Browse the repository at this point in the history
  • Loading branch information
simonask committed Jan 15, 2025
1 parent 731a978 commit d935d64
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 8 deletions.
59 changes: 59 additions & 0 deletions tests/test_outdatedness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@ build "glob-dep" {
}
"#;

static WERK_GLOBAL: &str = r#"
let arg = "a"
build "output" {
run {
write arg to "{out}"
}
}
"#;

static WERK_GLOBAL_CHANGED: &str = r#"
let args = ["b"]
let arg = "{args*}"
build "output" {
run {
write arg to "{out}"
}
}
"#;

#[apply(smol_macros::test)]
async fn test_outdated_env() -> anyhow::Result<()> {
_ = tracing_subscriber::fmt::try_init();
Expand Down Expand Up @@ -413,3 +434,41 @@ async fn test_outdated_define() -> anyhow::Result<()> {

Ok(())
}

#[apply(smol_macros::test)]
async fn test_outdated_global_constant() -> anyhow::Result<()> {
_ = tracing_subscriber::fmt::try_init();

let mut test = Test::new(WERK_GLOBAL)?;
let workspace = test.create_workspace(&[])?;
let runner = werk_runner::Runner::new(&workspace);
let status = runner.build_file(Path::new("output")?).await?;

assert_eq!(
status,
BuildStatus::Complete(
TaskId::build(Absolute::try_from("/output").unwrap()),
Outdatedness::new([Reason::Missing(PathBuf::try_from("/output")?),])
)
);
workspace.finalize().await?;
std::mem::drop(runner);

test.reload(WERK_GLOBAL_CHANGED)?;
let workspace = test.create_workspace(&[])?;
let runner = werk_runner::Runner::new(&workspace);
let status = runner.build_file(Path::new("output")?).await?;

assert_eq!(
status,
BuildStatus::Complete(
TaskId::build(Absolute::try_from("/output").unwrap()),
Outdatedness::new([
Reason::GlobalChanged(String::from("arg")),
Reason::GlobalChanged(String::from("args"))
])
)
);

Ok(())
}
10 changes: 10 additions & 0 deletions werk-runner/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ pub struct TargetOutdatednessCache {
/// Hash of environment variables.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub env: BTreeMap<String, Hash128>,
/// Hash of the definitions (AST expressions) of global variables used.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub global: BTreeMap<String, Hash128>,
/// Hash of `define` variables.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub define: BTreeMap<String, Hash128>,
Expand Down Expand Up @@ -59,6 +62,13 @@ impl TargetOutdatednessCache {
.get(define)
.map_or(true, |old_hash| *old_hash != new_hash)
}

#[inline]
pub fn is_global_outdated(&self, var: &str, new_hash: Hash128) -> bool {
self.global
.get(var)
.map_or(true, |old_hash| *old_hash != new_hash)
}
}

impl rustc_stable_hash::FromStableHash for Hash128 {
Expand Down
6 changes: 5 additions & 1 deletion werk-runner/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ impl<T> Eval<T> {
}

pub fn using_var(value: T, used: UsedVariable) -> Self {
Self::using_vars(value, [used])
}

pub fn using_vars(value: T, used: impl IntoIterator<Item = UsedVariable>) -> Self {
Self {
value,
used: Used::from_iter(Some(used)),
used: Used::from_iter(used),
}
}

Expand Down
3 changes: 3 additions & 0 deletions werk-runner/eval/used.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ pub enum UsedVariable {
Which(String, Hash128),
Env(String, Hash128),
Define(String, Hash128),
/// Used a global variable. The hash is the hash of the expression (not the
/// value itself).
Global(String, Hash128),
WorkspaceFile(werk_fs::Absolute<werk_fs::PathBuf>, std::time::SystemTime),
}

Expand Down
15 changes: 14 additions & 1 deletion werk-runner/outdatedness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ pub enum Reason {
Env(String),
/// The resolved path of a binary executable changed between runs.
Which(String),
/// Recipe changed.
/// The constant value of a global variable changed between runs.
GlobalChanged(String),
/// Recipe changed between runs.
RecipeChanged,
/// Manual define changed.
Define(String),
Expand Down Expand Up @@ -126,6 +128,7 @@ impl std::fmt::Display for Reason {
Reason::Env(env) => write!(f, "environment variable `{env}` changed"),
Reason::Which(program) => write!(f, "resolved path of `{program}` changed"),
Reason::RecipeChanged => f.write_str("recipe changed"),
Reason::GlobalChanged(variable) => write!(f, "global variable `{variable}` changed"),
Reason::Define(define) => write!(f, "variable `{define}` was manually overridden"),
Reason::Rebuilt(task_id) => {
if task_id.is_command() {
Expand Down Expand Up @@ -165,6 +168,7 @@ impl<'a> OutdatednessTracker<'a> {
which: Default::default(),
env: Default::default(),
define: Default::default(),
global: Default::default(),
};

Self {
Expand Down Expand Up @@ -214,6 +218,15 @@ impl<'a> OutdatednessTracker<'a> {
}
self.new_cache.define.insert(def.clone(), hash);
}
UsedVariable::Global(var, hash) => {
if self
.cache
.is_some_and(|cache| cache.is_global_outdated(&var, hash))
{
self.outdatedness.insert(Reason::GlobalChanged(var.clone()));
}
self.new_cache.global.insert(var.clone(), hash);
}
UsedVariable::WorkspaceFile(path, mtime) => {
if let Some(target_mtime) = self.target_mtime {
if mtime > target_mtime {
Expand Down
19 changes: 13 additions & 6 deletions werk-runner/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ impl<'a> Workspace<'a> {
continue;
}
ast::RootStmt::Let(ref let_stmt) => {
let hash = compute_stable_semantic_hash(&let_stmt.value);
if let Some(global_override) = self.defines.get(let_stmt.ident.ident) {
tracing::trace!(
"Overriding global variable `{}` with `{}`",
Expand All @@ -217,19 +218,25 @@ impl<'a> Workspace<'a> {
self.manifest.globals.insert(
let_stmt.ident.ident.to_owned(),
GlobalVar {
value: Eval::using_var(
value: Eval::using_vars(
global_override.clone().into(),
UsedVariable::Define(
let_stmt.ident.ident.to_owned(),
compute_stable_hash(global_override),
),
[
UsedVariable::Global(let_stmt.ident.ident.to_owned(), hash),
UsedVariable::Define(
let_stmt.ident.ident.to_owned(),
compute_stable_hash(global_override),
),
],
),
comment: doc_comment,
},
);
} else {
let scope = RootScope::new(self);
let value = eval::eval(&scope, &let_stmt.value)?;
let mut value = eval::eval(&scope, &let_stmt.value)?;
value
.used
.insert(UsedVariable::Global(let_stmt.ident.ident.to_owned(), hash));
tracing::trace!("(global) let `{}` = {:?}", let_stmt.ident, value);
self.manifest.globals.insert(
let_stmt.ident.ident.to_owned(),
Expand Down

0 comments on commit d935d64

Please sign in to comment.