Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: prefer tectonic-biber as the biber executable #1166

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
7 changes: 6 additions & 1 deletion docs/src/ref/v2cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
39 changes: 36 additions & 3 deletions src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use tectonic_io_base::{
stdstreams::{BufferedPrimaryIo, GenuineStdoutIo},
InputHandle, IoProvider, OpenResult, OutputHandle,
};
use which::which;

use crate::{
ctry, errmsg,
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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<Option<ExternalToolPass>> {
fn check_biber_requirement(
&self,
status: &mut dyn StatusBackend,
) -> Result<Option<ExternalToolPass>> {
// Is there a `.run.xml` file?

let mut run_xml_path = PathBuf::from(&self.primary_input_tex_path);
Expand All @@ -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<String> {
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
Expand Down Expand Up @@ -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,
Expand Down
56 changes: 30 additions & 26 deletions tests/executable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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);
}

Expand All @@ -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
Expand All @@ -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
Expand Down
8 changes: 8 additions & 0 deletions tests/executable/tectonic-biber
Original file line number Diff line number Diff line change
@@ -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
Loading