From 2a3ed8d950d597d98e65a4bf9e9306aa853241c1 Mon Sep 17 00:00:00 2001 From: Bryan Lai Date: Thu, 29 Feb 2024 21:36:40 +0800 Subject: [PATCH 1/2] Prefer `tectonic-biber` as the biber executable When an executable called `tectonic-biber` is found, either in the current working directory or in `$PATH`, prefer that as the biber executable over the plain old `biber`. This may help resolve possible version mismatch between `biber` and the bundled `biblatex` files when there are multiple TeX installations on a system. --- Cargo.lock | 1 + Cargo.toml | 1 + docs/src/ref/v2cli.md | 7 ++++++- src/driver.rs | 39 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91a03d4f6..1a1a30742 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2641,6 +2641,7 @@ dependencies = [ "watchexec-filterer-globset", "watchexec-signals", "watchexec-supervisor", + "which", "zip", ] diff --git a/Cargo.toml b/Cargo.toml index f63747a40..1162c566c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ watchexec = "^3.0" watchexec-filterer-globset = "3.0" watchexec-signals = "2.0" watchexec-supervisor = "1.0" +which = "^4.4" zip = { version = "^0.6", default-features = false, features = ["deflate"] } time = "0.3.36" diff --git a/docs/src/ref/v2cli.md b/docs/src/ref/v2cli.md index 4d18d794e..dac245e0d 100644 --- a/docs/src/ref/v2cli.md +++ b/docs/src/ref/v2cli.md @@ -45,8 +45,13 @@ or symlink the `tectonic` binary to `nextonic` manually. The V2 interface also supports external commands. If you run `tectonic -X cmd`, where `cmd` is NOT built into Tectonic, Tectonic will search for a binary called `tectonic-cmd` and run it if it exists. +In particular, if a `tectonic-biber` binary is found it will be preferred over +the regular `biber` binary when generating bibliography with the `biblatex` +package. This may help resolve [possible version mismatch][biber-mismatch] +between `biber` and the bundled `biblatex` files when there are multiple TeX +installations on a system. - +[biber-mismatch]: https://github.com/tectonic-typesetting/tectonic/issues/893 ## Migration plan diff --git a/src/driver.rs b/src/driver.rs index 320ef64fb..ba61d2dfe 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -37,6 +37,7 @@ use tectonic_io_base::{ stdstreams::{BufferedPrimaryIo, GenuineStdoutIo}, InputHandle, IoProvider, OpenResult, OutputHandle, }; +use which::which; use crate::{ ctry, errmsg, @@ -1682,7 +1683,7 @@ impl ProcessingSession { Some(RerunReason::Bibtex) } else { warnings = self.tex_pass(None, status)?; - let maybe_biber = self.check_biber_requirement()?; + let maybe_biber = self.check_biber_requirement(status)?; if let Some(biber) = maybe_biber { self.bs.external_tool_pass(&biber, status)?; @@ -2033,7 +2034,10 @@ impl ProcessingSession { /// `loqreq` package to figure out what files `biber` needs. This /// functionality should probably become more generic, but I don't have a /// great sense as to how widely-used `logreq` is. - fn check_biber_requirement(&self) -> Result> { + fn check_biber_requirement( + &self, + status: &mut dyn StatusBackend, + ) -> Result> { // Is there a `.run.xml` file? let mut run_xml_path = PathBuf::from(&self.primary_input_tex_path); @@ -2056,10 +2060,36 @@ impl ProcessingSession { ); let mut argv = match s { - (true, Ok(text)) => text.split_whitespace().map(|x| x.to_owned()).collect(), + (true, Ok(text)) if !text.trim().is_empty() => { + text.split_whitespace().map(|x| x.to_owned()).collect() + } + // when `TECTONIC_TEST_FAKE_BIBER` is empty, proceed to discover + // the biber binary as follows. _ => vec!["biber".to_owned()], }; + // Moreover, we allow an override of the biber executable, to cope with + // possible version mismatch of the bundled biblatex package, as filed + // in issue #893. Since PR #1103, the `tectonic-biber` override can + // also be invoked with `tectonic -X biber`. + let find_by = |binary_name: &str| -> Option { + if let Ok(pathbuf) = which(binary_name) { + if let Some(biber_path) = pathbuf.to_str() { + return Some(biber_path.to_owned()); + } + } + None + }; + + let mut use_tectonic_biber_override = false; + for binary_name in ["./tectonic-biber", "tectonic-biber"] { + if let Some(biber_path) = find_by(binary_name) { + argv = vec![biber_path]; + use_tectonic_biber_override = true; + break; + } + } + let mut extra_requires = HashSet::new(); // Do a sketchy XML parse to see if there's info about a biber @@ -2205,6 +2235,9 @@ impl ProcessingSession { // No biber invocation, in the end. None } else { + if use_tectonic_biber_override { + tt_note!(status, "using `tectonic-biber`, found at {}", argv[0]); + } Some(ExternalToolPass { argv, extra_requires, From e45284f8cd8686dead98884a9f1b5b70d49d5d19 Mon Sep 17 00:00:00 2001 From: Bryan Lai Date: Thu, 29 Feb 2024 22:01:51 +0800 Subject: [PATCH 2/2] Add test for `tectonic-biber` with cleanups In particular, refactor the biber tests in tests/executable to reduce duplicated code. --- tests/executable.rs | 56 ++++++++++++++++++--------------- tests/executable/tectonic-biber | 8 +++++ 2 files changed, 38 insertions(+), 26 deletions(-) create mode 100755 tests/executable/tectonic-biber diff --git a/tests/executable.rs b/tests/executable.rs index a70c748fe..f986735a5 100644 --- a/tests/executable.rs +++ b/tests/executable.rs @@ -303,11 +303,17 @@ fn bad_outfmt_1() { } fn run_with_biber(args: &str, stdin: &str) -> Output { + run_with_biber_exe(None, args, stdin, &["subdirectory/empty.bib"]) +} + +fn run_with_biber_exe(executable: Option<&str>, args: &str, stdin: &str, files: &[&str]) -> Output { let fmt_arg = get_plain_format_arg(); - let tempdir = setup_and_copy_files(&["subdirectory/empty.bib"]); + let tempdir = setup_and_copy_files(files); let mut command = prep_tectonic(tempdir.path(), &[&fmt_arg, "-"]); - let test_cmd = if cfg!(windows) { + let test_cmd = if let Some(exe) = executable { + format!("{} {}", exe, args) + } else if cfg!(windows) { format!( "cmd /c {} {}", util::test_path(&["fake-biber.bat"]).display(), @@ -393,28 +399,9 @@ fn biber_failure() { #[test] fn biber_no_such_tool() { - let fmt_arg = get_plain_format_arg(); - let tempdir = setup_and_copy_files(&[]); - let mut command = prep_tectonic(tempdir.path(), &[&fmt_arg, "-"]); - - command.env("TECTONIC_TEST_FAKE_BIBER", "ohnothereisnobiberprogram"); - const REST: &str = r"\bye"; let tex = format!("{BIBER_TRIGGER_TEX}{REST}"); - - command - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - println!("running {command:?}"); - let mut child = command.spawn().expect("tectonic failed to start"); - - write!(child.stdin.as_mut().unwrap(), "{tex}") - .expect("failed to send data to tectonic subprocess"); - - let output = child - .wait_with_output() - .expect("failed to wait on tectonic subprocess"); + let output = run_with_biber_exe(Some("ohnothereisnobiberprogram"), "", &tex, &[]); error_or_panic(&output); } @@ -425,9 +412,7 @@ fn biber_signal() { error_or_panic(&output); } -#[test] -fn biber_success() { - const REST: &str = r" +const BIBER_VALIDATE_TEX: &str = r" \ifsecond \ifnum\input{biberout.qqq}=456\relax a @@ -436,11 +421,30 @@ a \fi \fi \bye"; - let tex = format!("{BIBER_TRIGGER_TEX}{REST}"); + +#[test] +fn biber_success() { + let tex = format!("{BIBER_TRIGGER_TEX}{BIBER_VALIDATE_TEX}"); let output = run_with_biber("success", &tex); success_or_panic(&output); } +/// Test `tectonic-biber` override: when no args passed, fall back to $PATH +/// lookup for `tectonic-biber` first, and then `biber`. Currently defined in: +/// [`tectonic::driver::ProcessingSession::check_biber_requirement`] +#[cfg(unix)] +#[test] +fn biber_tectonic_override() { + let tex = format!("{BIBER_TRIGGER_TEX}{BIBER_VALIDATE_TEX}"); + let output = run_with_biber_exe( + Some(""), + "", // no args passed + &tex, + &["subdirectory/empty.bib", "tectonic-biber"], + ); + success_or_panic(&output); +} + /// #844: biber input with absolute path blows away the file /// /// We need to create a separate temporary directory to see if the abspath input diff --git a/tests/executable/tectonic-biber b/tests/executable/tectonic-biber new file mode 100755 index 000000000..73c4da308 --- /dev/null +++ b/tests/executable/tectonic-biber @@ -0,0 +1,8 @@ +#! /bin/sh +# Copyright 2021 the Tetonic Project +# Licensed under the MIT License. + +# A stand-in for biber for our testing framework. + +echo "tectonic-biber says success and makes a file" +echo 456 >biberout.qqq