From b162ce1d581d2f3352a2d878e037bd9949e9dace Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Wed, 14 Apr 2021 23:50:30 -0600 Subject: rebuild out-of-date makefiles, kinda --- src/main.rs | 29 ++++++++++++- src/makefile/input.rs | 112 ++++++++++++++++++++++++++++++++++++++++++------- src/makefile/mod.rs | 4 +- src/makefile/target.rs | 2 +- 4 files changed, 127 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index c5bc049..8fa4efd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,7 @@ use std::env; use std::fs::metadata; use std::io::stdin; use std::path::PathBuf; +use std::rc::Rc; use eyre::{bail, Result}; @@ -63,18 +64,42 @@ fn main() -> Result<()> { // TODO dump command-line macros into environment // TODO add SHELL macro let mut makefile = Makefile::new(&args); + let paths = Default::default(); for filename in &args.makefile { if filename == &PathBuf::from("-") { let macros = makefile.macros.with_overlay(); - let file = MakefileReader::read(&args, macros, stdin().lock(), "-")?.finish(); + let file = MakefileReader::read(&args, macros, stdin().lock(), "-", Rc::clone(&paths))? + .finish(); makefile.extend(file)?; } else { let macros = makefile.macros.with_overlay(); - let file = MakefileReader::read_file(&args, macros, filename)?.finish(); + let file = + MakefileReader::read_file(&args, macros, filename, Rc::clone(&paths))?.finish(); makefile.extend(file)?; }; } + let makefiles_outdated = paths + .borrow() + .iter() + .filter(|path| { + makefile + .get_target(path) + .map_or(false, |target| !target.borrow().is_up_to_date(&makefile)) + }) + .cloned() + .collect::>(); + for outdated in makefiles_outdated { + eprintln!("makefile {} out of date, rebuilding", outdated); + makefile.update_target(&outdated)?; + let macros = makefile.macros.with_overlay(); + let file = + MakefileReader::read_file(&args, macros, &outdated, Default::default())?.finish(); + // TODO forget the stale data + // TODO reread all the things, not just this one + makefile.extend(file)?; + } + if args.print_everything { println!("{}", &makefile); } else { diff --git a/src/makefile/input.rs b/src/makefile/input.rs index 4a0f086..64b12d7 100644 --- a/src/makefile/input.rs +++ b/src/makefile/input.rs @@ -1,10 +1,11 @@ -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::error::Error as StdError; use std::fs::File; use std::io::{BufRead, BufReader, Cursor, Error as IoError, ErrorKind as IoErrorKind, Lines}; use std::iter::Peekable; use std::path::Path; +use std::rc::Rc; use eyre::{bail, eyre, Context, Result}; use lazy_static::lazy_static; @@ -192,6 +193,7 @@ pub struct MakefileReader<'a, 'parent, R: BufRead> { pending_line: Option<(usize, Vec)>, #[cfg(feature = "full")] conditional_stack: Vec, + file_names: Rc>>, } impl<'a, 'parent> MakefileReader<'a, 'parent, BufReader> { @@ -199,6 +201,7 @@ impl<'a, 'parent> MakefileReader<'a, 'parent, BufReader> { args: &'a Args, mut macros: MacroSet<'parent, 'static>, path: impl AsRef, + file_names: Rc>>, ) -> Result { #[cfg(feature = "full")] if let Some(mut old_makefile_list) = macros.pop("MAKEFILE_LIST") { @@ -218,11 +221,13 @@ impl<'a, 'parent> MakefileReader<'a, 'parent, BufReader> { }, ); } + let file_name = path.as_ref().to_string_lossy(); + file_names.borrow_mut().push(file_name.to_string()); let file = File::open(path.as_ref()); // TODO handle errors let file = file.context("couldn't open makefile!")?; let file_reader = BufReader::new(file); - Self::read(args, macros, file_reader, path.as_ref().to_string_lossy()) + Self::read(args, macros, file_reader, file_name, file_names) } } @@ -232,6 +237,7 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> { macros: MacroSet<'parent, 'static>, source: R, name: impl Into, + file_names: Rc>>, ) -> Result { let name = name.into(); let mut reader = Self { @@ -247,6 +253,7 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> { pending_line: None, #[cfg(feature = "full")] conditional_stack: Vec::new(), + file_names, }; // TODO be smart about this instead, please if !args.no_builtin_rules { @@ -310,6 +317,7 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> { child_macros, Cursor::new(eval), "", + Rc::clone(&self.file_names), ) .context("while evaling")? .finish(); @@ -536,9 +544,15 @@ impl<'a, 'parent, R: BufRead> MakefileReader<'a, 'parent, R> { // POSIX says we only have to handle a single filename, but GNU make // handles arbitrarily many filenames, and it's not like that's more work for field in fields { + log::trace!("{}:{}: including {}", &self.file_name, line_number, field); let child_macros = self.macros.with_overlay(); - let child = MakefileReader::read_file(self.args, child_macros, field) - .with_context(|| format!("while including {}", field)); + let child = MakefileReader::read_file( + self.args, + child_macros, + field, + Rc::clone(&self.file_names), + ) + .with_context(|| format!("while including {}", field)); match child { Ok(child) => { let child = child.finish(); @@ -957,8 +971,14 @@ a: $(x) b \\ \t\td \tfoo"; let args = Args::empty(); - let makefile = - MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?.finish(); + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )? + .finish(); assert_eq!( makefile.targets["a"].prerequisites, vec!["3", "4", "5", "b", "c", "d"] @@ -979,7 +999,13 @@ worked = perhaps endif "; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; assert_eq!( makefile.expand_macros(&TokenString::r#macro("worked"))?, "yes" @@ -997,7 +1023,13 @@ ifeq (1,1) endif "; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; let makefile = makefile.finish(); assert_eq!(makefile.targets["a"].commands.len(), 1); Ok(()) @@ -1013,7 +1045,13 @@ baz endef "; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; assert_eq!( makefile.expand_macros(&TokenString::r#macro("foo"))?, "bar\nbaz" @@ -1035,7 +1073,13 @@ endif FOO = bar "; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; assert_eq!(makefile.expand_macros(&TokenString::r#macro("FOO"))?, "bar",); Ok(()) } @@ -1077,7 +1121,13 @@ clean: "; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; let makefile = makefile.finish(); assert!(makefile.targets.contains_key("server")); Ok(()) @@ -1092,7 +1142,13 @@ info: \thello # there "; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; let makefile = makefile.finish(); assert_eq!( makefile.targets["foo"], @@ -1136,7 +1192,13 @@ cursed: \techo yeah its value is $$# and it's really cool "; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; let _makefile = makefile.finish(); Ok(()) } @@ -1154,7 +1216,13 @@ cursed: \techo hiiii "; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; let makefile = makefile.finish(); assert_eq!(makefile.inference_rules.len(), 2); Ok(()) @@ -1169,7 +1237,13 @@ test: a test: c "; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; let makefile = makefile.finish(); assert_eq!( makefile.targets["test"].prerequisites, @@ -1183,7 +1257,13 @@ test: c fn export_assign() -> R { let file = "export x = 3"; let args = Args::empty(); - let makefile = MakefileReader::read(&args, MacroSet::new(), Cursor::new(file), "")?; + let makefile = MakefileReader::read( + &args, + MacroSet::new(), + Cursor::new(file), + "", + Default::default(), + )?; let makefile = makefile.finish(); assert_eq!( makefile.macros.get("x").map(|x| &x.text), diff --git a/src/makefile/mod.rs b/src/makefile/mod.rs index 7cc16c5..981636b 100644 --- a/src/makefile/mod.rs +++ b/src/makefile/mod.rs @@ -148,7 +148,9 @@ impl<'a> Makefile<'a> { format!("while building missing included file {}", &failed_include) })?; let macros = self.macros.with_overlay(); - let file = MakefileReader::read_file(self.args, macros, failed_include)?.finish(); + let file = + MakefileReader::read_file(self.args, macros, failed_include, Default::default())? + .finish(); self.extend(file)?; } Ok(()) diff --git a/src/makefile/target.rs b/src/makefile/target.rs index c12f666..c3431e4 100644 --- a/src/makefile/target.rs +++ b/src/makefile/target.rs @@ -67,7 +67,7 @@ impl Target { }) } - fn is_up_to_date(&self, file: &Makefile) -> bool { + pub fn is_up_to_date(&self, file: &Makefile) -> bool { if self.already_updated.get() { return true; } -- cgit v1.2.3