use std::path::{Path, PathBuf}; use syntax::latexmkrc::LatexmkrcData; mod v483 { use std::path::Path; use syntax::latexmkrc::LatexmkrcData; use tempfile::tempdir; use crate::latexmkrc::change_root; pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result { let temp_dir = tempdir()?; let non_existent_tex = temp_dir.path().join("NONEXISTENT.tex"); std::fs::write(temp_dir.path().join(".latexmkrc"), input)?; // Run `latexmk -dir-report $TMPDIR/NONEXISTENT.tex` to obtain out_dir // and aux_dir values. We pass nonexistent file to prevent latexmk from // building anything, since we need this invocation only to extract the // -dir-report variables. // // In later versions, latexmk provides the -dir-report-only option and we // won't have to resort to this hack with NONEXISTENT.tex. let output = std::process::Command::new("latexmk") .arg("-dir-report") .arg(non_existent_tex) .current_dir(temp_dir.path()) .output()?; let stdout = String::from_utf8_lossy(&output.stdout); let (aux_dir, out_dir) = stdout.lines().find_map(extract_dirs).ok_or_else(|| { std::io::Error::new( std::io::ErrorKind::InvalidData, "Normalized aux and out dir were not found in latexmk output", ) })?; let aux_dir = change_root(src_dir, temp_dir.path(), &aux_dir); let out_dir = change_root(src_dir, temp_dir.path(), &out_dir); Ok(LatexmkrcData { aux_dir, out_dir }) } /// Extracts $aux_dir and $out_dir from lines of the form /// /// Latexmk: Normalized aux dir and out dir: '$aux_dir', '$out_dir' fn extract_dirs(line: &str) -> Option<(String, String)> { let mut it = line .strip_prefix("Latexmk: Normalized aux dir and out dir: ")? .split(", "); let aux_dir = it.next()?.strip_prefix('\'')?.strip_suffix('\'')?; let out_dir = it.next()?.strip_prefix('\'')?.strip_suffix('\'')?; // Ensure there's no more data if it.next().is_some() { return None; } Some((String::from(aux_dir), String::from(out_dir))) } } mod v484 { use std::{path::Path, str::Lines}; use syntax::latexmkrc::LatexmkrcData; use tempfile::tempdir; use super::change_root; pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result { let temp_dir = tempdir()?; std::fs::write(temp_dir.path().join(".latexmkrc"), input)?; // Create an empty dummy TeX file to let latexmk continue std::fs::write(temp_dir.path().join("dummy.tex"), "")?; // Run `latexmk -dir-report-only` to obtain out_dir and aux_dir values. let output = std::process::Command::new("latexmk") .arg("-dir-report-only") .current_dir(temp_dir.path()) .output()?; let stdout = String::from_utf8_lossy(&output.stdout); let (aux_dir, out_dir) = extract_dirs(stdout.lines()).ok_or_else(|| { std::io::Error::new( std::io::ErrorKind::InvalidData, "Normalized aux and out dir were not found in latexmk output", ) })?; let aux_dir = change_root(src_dir, temp_dir.path(), &aux_dir); let out_dir = change_root(src_dir, temp_dir.path(), &out_dir); Ok(LatexmkrcData { aux_dir, out_dir }) } /// Extracts $aux_dir and $out_dir from lines of the form /// /// Latexmk: Normalized aux dir and out dirs: /// '$aux_dir', '$out_dir', [...] fn extract_dirs(lines: Lines) -> Option<(String, String)> { let mut it = lines .skip_while(|line| !line.starts_with("Latexmk: Normalized aux dir and out dirs:")) .nth(1)? .split(','); let aux_dir = it.next()?.trim().strip_prefix('\'')?.strip_suffix('\'')?; it.next(); // Skip the old 'outdir' option. let out_dir = it.next()?.trim().strip_prefix('\'')?.strip_suffix('\'')?; // Ensure there's no more data if it.next().is_some() { return None; } Some((String::from(aux_dir), String::from(out_dir))) } } pub fn parse_latexmkrc(input: &str, src_dir: &Path) -> std::io::Result { let output = std::process::Command::new("latexmk") .arg("--version") .output()?; let version = String::from_utf8(output.stdout) .ok() .as_ref() .and_then(|line| Some((line.find("Version")?, line))) .and_then(|(i, line)| line[i..].trim_end().strip_prefix("Version ")) .and_then(versions::Versioning::new); let result = if version.map_or(false, |v| v >= versions::Versioning::new("4.84").unwrap()) { v484::parse_latexmkrc(input, src_dir) } else { v483::parse_latexmkrc(input, src_dir) }; log::debug!("Latexmkrc parsing result: src_dir={src_dir:?}, output={result:?}"); result } fn change_root(src_dir: &Path, tmp_dir: &Path, out_dir: &str) -> Option { let out_dir = tmp_dir.join(out_dir); let relative_to_tmp = pathdiff::diff_paths(out_dir, tmp_dir)?; let relative_to_src = pathdiff::diff_paths(src_dir.join(relative_to_tmp), src_dir)?; if relative_to_src == PathBuf::new() { return None; } Some(relative_to_src.to_str()?.to_string()) }