From 86377c1038d44b2a578460568a8e859dcd8d0adc Mon Sep 17 00:00:00 2001 From: rsheeter Date: Wed, 11 Dec 2024 15:53:19 -0800 Subject: [PATCH] Further hackery --- README.md | 2 +- fontc/src/args.rs | 4 +- fontc/src/lib.rs | 90 ++++++++++++++++--------------------------- fontc/src/workload.rs | 53 ++++++++++++++----------- 4 files changed, 69 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 993f4a09..f954cec3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Where in we pursue oxidizing [fontmake](https://github.com/googlefonts/fontmake) For context around where fontmake came from see [Mr B goes to Vartown](https://github.com/googlefonts/oxidize/blob/main/text/2023-10-18-mrb-goes-to-vartown.md). -Converts source to IR, and then IR to font binary. Aims to be safe, incremental, and fast. +Converts source to IR, and then IR to font binary. Aims to be safe and fast. References diff --git a/fontc/src/args.rs b/fontc/src/args.rs index 3956f995..be33a7cd 100644 --- a/fontc/src/args.rs +++ b/fontc/src/args.rs @@ -152,7 +152,9 @@ impl Args { use crate::testdata_dir; let input_source = testdata_dir().join(source).canonicalize().unwrap(); - Self::new(build_dir, input_source) + let mut result = Self::new(build_dir, input_source); + result.emit_ir = true; + result } /// The input source to compile. diff --git a/fontc/src/lib.rs b/fontc/src/lib.rs index e3850049..4cc1ff66 100644 --- a/fontc/src/lib.rs +++ b/fontc/src/lib.rs @@ -173,37 +173,41 @@ fn add_feature_ir_job(workload: &mut Workload) -> Result<(), Error> { } fn add_feature_comp_be_job(workload: &mut Workload) -> Result<(), Error> { + let work = FeatureCompilationWork::create(); if !workload.change_detector.should_skip_features() { - workload.add(FeatureCompilationWork::create()); + workload.add(work); } else { - todo!("skip features; mark complete"); + workload.skip(work); } Ok(()) } fn add_marks_be_job(workload: &mut Workload) -> Result<(), Error> { + let work = create_mark_work(); if !workload.change_detector.should_skip_features() { - workload.add(create_mark_work()); + workload.add(work); } else { - todo!("skip features; mark complete"); + workload.skip(work); } Ok(()) } fn add_gather_ir_kerning_be_job(workload: &mut Workload) -> Result<(), Error> { + let work = create_gather_ir_kerning_work(); if !workload.change_detector.should_skip_features() { - workload.add(create_gather_ir_kerning_work()); + workload.add(work); } else { - todo!("skip features; mark complete"); + workload.skip(work); } Ok(()) } fn add_kerns_be_job(workload: &mut Workload) -> Result<(), Error> { + let work = create_kerns_work(); if !workload.change_detector.should_skip_features() { - workload.add(create_kerns_work()); + workload.add(work); } else { - todo!("skip features; mark complete"); + workload.skip(work); } Ok(()) } @@ -395,7 +399,6 @@ mod tests { /// the directory is deleted. _temp_dir: TempDir, build_dir: PathBuf, - args: Args, work_executed: HashSet, fe_context: FeContext, be_context: BeContext, @@ -407,12 +410,6 @@ mod tests { TestCompile::compile(source, |args| args) } - fn compile_again(prior_compile: &TestCompile) -> TestCompile { - TestCompile::compile(prior_compile.args.source().to_str().unwrap(), |_| { - prior_compile.args.clone() - }) - } - fn compile(source: &str, adjust_args: impl Fn(Args) -> Args) -> TestCompile { let mut timer = JobTimer::new(Instant::now()); let _ = env_logger::builder().is_test(true).try_init(); @@ -435,7 +432,6 @@ mod tests { let mut result = TestCompile { _temp_dir: temp_dir, build_dir, - args: args.clone(), work_executed: HashSet::new(), fe_context, be_context, @@ -491,8 +487,8 @@ mod tests { &mut File::open(build_dir.join("loca.format")).unwrap(), ) .into(), - raw_glyf: read_file(&build_dir.join("glyf.table")), - raw_loca: read_file(&build_dir.join("loca.table")), + raw_glyf: read_file(build_dir, Path::new("glyf.table")), + raw_loca: read_file(build_dir, Path::new("loca.table")), } } @@ -510,39 +506,6 @@ mod tests { } } - /// Copy testdata => tempdir so the test can modify it - fn copy_testdata(from: impl IntoIterator>, to_dir: &Path) { - let from_dir = testdata_dir(); - - let mut from: VecDeque = from.into_iter().map(|p| from_dir.join(p)).collect(); - while let Some(source) = from.pop_back() { - let rel_source = source.strip_prefix(&from_dir).unwrap(); - let dest = to_dir.join(rel_source); - assert!( - source.exists(), - "cannot copy '{source:?}'; it doesn't exist" - ); - - let dest_dir = if source.is_dir() { - dest.as_path() - } else { - dest.parent().unwrap() - }; - if !dest_dir.exists() { - fs::create_dir_all(dest_dir).unwrap(); - } - - if source.is_file() { - fs::copy(&source, &dest).unwrap(); - } - if source.is_dir() { - for entry in fs::read_dir(source).unwrap() { - from.push_back(entry.unwrap().path()); - } - } - } - } - fn assert_compile_work(source: &str, glyphs: Vec<&str>) { let result = TestCompile::compile_source(source); let mut completed = result.work_executed.iter().cloned().collect::>(); @@ -689,7 +652,22 @@ mod tests { (result, (*glyph).clone()) } - fn read_file(path: &Path) -> Vec { + fn read_file(build_dir: &Path, path: &Path) -> Vec { + assert!(build_dir.is_dir(), "{build_dir:?} isn't a directory?!"); + let path = build_dir.join(path); + if !path.exists() { + // When a path is missing it's very helpful to know what's present + eprintln!("Build dir tree"); + let mut pending = VecDeque::new(); + pending.push_back(build_dir); + while let Some(pending_dir) = pending.pop_front() { + for entry in fs::read_dir(pending_dir).unwrap() { + let entry = entry.unwrap(); + eprintln!("{}", entry.path().to_str().unwrap()); + } + } + + } assert!(path.exists(), "{path:?} not found"); let mut buf = Vec::new(); File::open(path).unwrap().read_to_end(&mut buf).unwrap(); @@ -698,8 +676,8 @@ mod tests { fn read_ir_glyph(build_dir: &Path, name: &str) -> ir::Glyph { let raw_glyph = read_file( - &build_dir - .join("glyph_ir") + build_dir, + &Path::new("glyph_ir") .join(string_to_filename(name, ".yml")), ); ir::Glyph::read(&mut raw_glyph.as_slice()) @@ -707,8 +685,8 @@ mod tests { fn read_be_glyph(build_dir: &Path, name: &str) -> RawGlyph { let raw_glyph = read_file( - &build_dir - .join("glyphs") + build_dir, + &Path::new("glyphs") .join(string_to_filename(name, ".glyf")), ); let read: &mut dyn Read = &mut raw_glyph.as_slice(); diff --git a/fontc/src/workload.rs b/fontc/src/workload.rs index b21cbb8b..0f53e963 100644 --- a/fontc/src/workload.rs +++ b/fontc/src/workload.rs @@ -133,6 +133,34 @@ impl<'a> Workload<'a> { ); } + /// Do all the task dependency bookkeeping but don't actually run the wokr + pub(crate) fn skip(&mut self, work: impl Into) { + let work: AnyWork = work.into(); + let id = work.id(); + self.insert_nop(id, work.read_access()); + } + + fn insert_with_bookkeeping(&mut self, job: Job) { + trace!("insert_job {}{:?}", job.work.as_ref().map(|_| "").unwrap_or("(nop) "), job.id); + + self.job_count += 1; + self.count_pending + .entry(job.id.discriminant()) + .or_default() + .fetch_add(1, Ordering::AcqRel); + self.jobs_pending.insert(job.id.clone(), job); + } + + fn insert_nop(&mut self, id: AnyWorkId, read_access: AnyAccess) { + self.insert_with_bookkeeping(Job { + id: id.clone(), + work: None, // we exist solely to be marked complete by also_complete + read_access, // We don't want to be deemed runnable prematurely + write_access: AnyAccess::Be(Access::None), + running: false, + }); + } + pub(crate) fn insert(&mut self, id: AnyWorkId, job: Job) { let also_completes = job .work @@ -142,32 +170,13 @@ impl<'a> Workload<'a> { // We need pending entries for also-completes items so dependencies on them work for id in also_completes.iter() { - self.jobs_pending.insert( - id.clone(), - Job { - id: id.clone(), - work: None, // we exist solely to be marked complete by also_complete - read_access: job.read_access.clone(), // We don't want to be deemed runnable prematurely - write_access: AnyAccess::Be(Access::None), - running: false, - }, - ); - self.count_pending - .entry(id.discriminant()) - .or_default() - .fetch_add(1, Ordering::AcqRel); + self.insert_nop(id.clone(), job.read_access.clone()); } - - self.job_count += 1 + also_completes.len(); - self.jobs_pending.insert(id.clone(), job); - self.count_pending - .entry(id.discriminant()) - .or_default() - .fetch_add(1, Ordering::AcqRel); - if !also_completes.is_empty() { self.also_completes.insert(id.clone(), also_completes); } + + self.insert_with_bookkeeping(job); } fn complete_one(&mut self, id: AnyWorkId, expected_pending: bool) {