Skip to content

Commit

Permalink
add a rudimentary '-d explain' tool
Browse files Browse the repository at this point in the history
Would have helped with #62.
  • Loading branch information
evmar committed Jun 15, 2023
1 parent eb38121 commit 5263f6c
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 22 deletions.
8 changes: 2 additions & 6 deletions src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,12 +425,8 @@ impl Hashes {
self.0.insert(id, hash);
}

pub fn changed(&self, id: BuildId, hash: Hash) -> bool {
let last_hash = match self.0.get(&id) {
None => return true,
Some(h) => *h,
};
hash != last_hash
pub fn get(&self, id: BuildId) -> Option<Hash> {
self.0.get(&id).copied()
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pub trait Progress {
/// Called when a task starts or completes.
fn task_state(&mut self, id: BuildId, build: &Build, result: Option<&TaskResult>);

/// Log some (debug) information, without corrupting the progress display.
fn log(&mut self, msg: String);

/// Called when the overall build has completed (success or failure), to allow
/// cleaning up the display.
fn finish(&mut self);
Expand Down Expand Up @@ -104,6 +107,11 @@ impl Progress for ConsoleProgress {
self.maybe_print_progress();
}

fn log(&mut self, msg: String) {
self.clear_progress();
println!("{}", msg);
}

fn flush(&mut self) {
self.print_progress();
}
Expand Down
21 changes: 12 additions & 9 deletions src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,27 @@ fn run_impl() -> anyhow::Result<i32> {

let args: Args = argh::from_env();

let mut options = work::Options {
parallelism: match args.parallelism {
Some(p) => p,
None => default_parallelism()?,
},
keep_going: args.keep_going,
explain: false,
};

if fake_ninja_compat && args.version {
println!("1.10.2");
return Ok(0);
}

if let Some(debug) = args.debug {
match debug.as_str() {
"explain" => options.explain = true,
"list" => {
println!("debug tools:");
println!(" trace generate json performance trace");
println!(" explain print why each target is considered out of date");
println!(" trace generate json performance trace");
return Ok(1);
}
"trace" => trace::open("trace.json")?,
Expand Down Expand Up @@ -151,14 +162,6 @@ fn run_impl() -> anyhow::Result<i32> {
std::env::set_current_dir(dir).map_err(|err| anyhow!("chdir {:?}: {}", dir, err))?;
}

let options = work::Options {
parallelism: match args.parallelism {
Some(p) => p,
None => default_parallelism()?,
},
keep_going: args.keep_going,
};

let mut progress: ConsoleProgress = ConsoleProgress::new(args.verbose, terminal::use_fancy());
match build(options, args.build_file, args.targets, &mut progress)? {
None => {
Expand Down
50 changes: 43 additions & 7 deletions src/work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,16 @@ impl BuildStates {
pub struct Options {
pub keep_going: usize,
pub parallelism: usize,
/// When true, verbosely explain why targets are considered dirty.
pub explain: bool,
}

pub struct Work<'a> {
graph: Graph,
db: db::Writer,
progress: &'a mut dyn Progress,
keep_going: usize,
explain: bool,
file_state: FileState,
last_hashes: Hashes,
build_states: BuildStates,
Expand All @@ -314,6 +317,7 @@ impl<'a> Work<'a> {
db,
progress,
keep_going: options.keep_going,
explain: options.explain,
file_state,
last_hashes,
build_states: BuildStates::new(build_count, pools),
Expand Down Expand Up @@ -484,7 +488,7 @@ impl<'a> Work<'a> {
/// Returns a build error if any required input files are missing.
/// Otherwise returns true if any expected but not required files,
/// e.g. outputs, are missing, implying that the build needs to be executed.
fn check_build_files_missing(&mut self, id: BuildId) -> anyhow::Result<bool> {
fn check_build_files_missing(&mut self, id: BuildId) -> anyhow::Result<Option<FileId>> {
{
let build = self.graph.build(id);
let phony = build.cmdline.is_none();
Expand Down Expand Up @@ -554,8 +558,9 @@ impl<'a> Work<'a> {
// For discovered_ins, ensure we have mtimes for them.
// But if they're missing, it isn't an error, it just means the build
// is dirty.
if self.ensure_discovered_stats(id)?.is_some() {
return Ok(true);
let missing = self.ensure_discovered_stats(id)?;
if missing.is_some() {
return Ok(missing);
}

// Stat all the outputs.
Expand All @@ -570,12 +575,12 @@ impl<'a> Work<'a> {
}
let mtime = self.file_state.restat(id, file.path())?;
if mtime == MTime::Missing {
return Ok(true);
return Ok(Some(id));
}
}

// All files accounted for.
Ok(false)
Ok(None)
}

/// Check a ready build for whether it needs to run, returning true if so.
Expand All @@ -593,15 +598,46 @@ impl<'a> Work<'a> {

// If any files are missing, the build is dirty without needing
// to consider hashes.
if file_missing {
if let Some(missing) = file_missing {
if self.explain {
self.progress.log(format!(
"explain: {}: input {} missing",
build.location,
self.graph.file(missing).name
));
}
return Ok(true);
}

// If we get here, all the relevant files are present and stat()ed,
// so compare the hash against the last hash.

// TODO: skip this whole function if no previous hash is present.
// More complex than just moving this block up, because we currently
// assume that we've always checked inputs after we've run a build.
let prev_hash = match self.last_hashes.get(id) {
None => {
if self.explain {
self.progress.log(format!(
"explain: {}: no previous state known",
build.location
));
}
return Ok(true);
}
Some(prev_hash) => prev_hash,
};

let hash = hash_build(&self.graph, &mut self.file_state, build)?;
Ok(self.last_hashes.changed(id, hash))
if prev_hash != hash {
if self.explain {
self.progress
.log(format!("explain: {}: input changed", build.location));
}
return Ok(true);
}

Ok(false)
}

/// Create the parent directories of a given list of fileids.
Expand Down

0 comments on commit 5263f6c

Please sign in to comment.