From 3f61689d2e476490f455aa12e460a89e19f4c674 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Wed, 17 Jun 2026 19:45:14 -0400 Subject: [PATCH 1/3] Consolidate test-ci.R into test-all.R Single test file means `CI=true Rscript tests/*.R` works directly without a for loop. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- tests/test-all.R | 4 ++++ tests/test-ci.R | 2 -- tests/test-cran.R | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 tests/test-all.R delete mode 100644 tests/test-ci.R delete mode 100644 tests/test-cran.R diff --git a/tests/test-all.R b/tests/test-all.R new file mode 100644 index 000000000..cc2ca9081 --- /dev/null +++ b/tests/test-all.R @@ -0,0 +1,4 @@ +library(testit) +test_pkg('tinytex', 'test-cran') +if (!is.na(Sys.getenv('CI', NA)) && tinytex:::tlmgr_available()) + test_pkg('tinytex', 'test-ci') diff --git a/tests/test-ci.R b/tests/test-ci.R deleted file mode 100644 index 2b581e14e..000000000 --- a/tests/test-ci.R +++ /dev/null @@ -1,2 +0,0 @@ -# run tests on CI servers (these tests depend on TeX Live) -if (!is.na(Sys.getenv('CI', NA)) && tinytex:::tlmgr_available()) testit::test_pkg('tinytex', 'test-ci') diff --git a/tests/test-cran.R b/tests/test-cran.R deleted file mode 100644 index 12ebe690c..000000000 --- a/tests/test-cran.R +++ /dev/null @@ -1 +0,0 @@ -testit::test_pkg('tinytex', 'test-cran') From 0e98366eefbcea5789b32b3fa11b00ad9e66221e Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Wed, 9 Sep 2026 00:00:51 -0400 Subject: [PATCH 2/3] Infer bib_engine from generated aux files The `bib_engine` argument of `latexmk()` defaulted to `bibtex`, so the bibliography of a biblatex + biber document was silently not built (bibtex requires \bibdata/\bibstyle in the .aux file, which biblatex + biber does not produce). Users had to manually set `bib_engine = 'biber'`. Default `bib_engine` to NULL and infer the engine from the auxiliary files generated during compilation: if a .bcf file exists (produced by biblatex with the biber backend), use biber; otherwise use bibtex. The explicit argument and the global option `tinytex.bib_engine` still take precedence. This reworks #19 to infer from the generated aux files rather than parsing the .tex source with regexes, which is more robust (handles multi-line \usepackage, \RequirePackage, etc.). Co-authored-by: HughParsonage Co-Authored-By: Claude Opus 4.8 --- NEWS.md | 4 ++++ R/latex.R | 18 ++++++++++++------ man/latexmk.Rd | 9 ++++++--- tests/test-ci/bib/biber.tex | 7 +++++++ tests/test-ci/bib/refs.bib | 8 ++++++++ tests/test-ci/test-latex.R | 11 +++++++++++ 6 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 tests/test-ci/bib/biber.tex create mode 100644 tests/test-ci/bib/refs.bib diff --git a/NEWS.md b/NEWS.md index 746798eda..d7fa0d987 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# CHANGES IN tinytex VERSION 0.61 + +- The `bib_engine` argument of `latexmk()` now defaults to `NULL`, in which case the bibliography engine is inferred from the auxiliary files generated during compilation: if a `.bcf` file is found (produced by **biblatex** with the `biber` backend), `biber` is used, otherwise `bibtex`. Previously the default was always `bibtex`, so the bibliography of a **biblatex** + **biber** document was silently not built unless you set `bib_engine = 'biber'` or the global option `tinytex.bib_engine` (thanks, @HughParsonage, #19). + # CHANGES IN tinytex VERSION 0.60 - Fixed the CTAN repository URL detection in `normalize_repo()`: when a URL like `https://tlnet.yihui.org` already serves `tlpkg/texlive.tlpdb` at its root, don't append `/systems/texlive/tlnet` to it. Also hardened `is_tlnet()` to check the `Content-Type` header instead of only the HTTP status code, so servers that return 200 with an HTML page for all URLs (e.g., Cloudflare Pages' SPA fallback) no longer fool the detection (thanks, @Gael-Hammer rstudio/tinytex-releases#59, @mfisher5 #508). diff --git a/R/latex.R b/R/latex.R index 5839700e1..cb43264d0 100644 --- a/R/latex.R +++ b/R/latex.R @@ -44,8 +44,11 @@ #' @param file A LaTeX file path. #' @param engine A LaTeX engine (can be set in the global option #' `tinytex.engine`, e.g., `options(tinytex.engine = 'xelatex')`). -#' @param bib_engine A bibliography engine (can be set in the global option -#' `tinytex.bib_engine`). +#' @param bib_engine A bibliography engine, either `'bibtex'` or `'biber'` (can +#' be set in the global option `tinytex.bib_engine`). By default (`NULL`), the +#' engine is inferred from the auxiliary files generated during compilation: +#' if a `.bcf` file is found (produced by \pkg{biblatex} with the `biber` +#' backend), `'biber'` is used; otherwise `'bibtex'` is used. #' @param engine_args Command-line arguments to be passed to `engine` (can #' be set in the global option `tinytex.engine_args`, e.g., #' `options(tinytex.engine_args = '-shell-escape')`). @@ -72,7 +75,7 @@ #' the `pdf_file` argument). latexmk = function( file, engine = c('pdflatex', 'xelatex', 'lualatex', 'latex', 'tectonic'), - bib_engine = c('bibtex', 'biber'), engine_args = NULL, emulation = TRUE, + bib_engine = NULL, engine_args = NULL, emulation = TRUE, min_times = 1, max_times = 10, install_packages = emulation && tlmgr_writable(), pdf_file = NULL, clean = TRUE ) { @@ -160,7 +163,7 @@ lualatex = function(...) latexmk(engine = 'lualatex', emulation = TRUE, ...) # a quick and dirty version of latexmk (should work reasonably well unless the # LaTeX document is extremely complicated) latexmk_emu = function( - file, engine, bib_engine = c('bibtex', 'biber'), engine_args = NULL, min_times = 1, max_times = 10, + file, engine, bib_engine = NULL, engine_args = NULL, min_times = 1, max_times = 10, install_packages = FALSE, clean ) { aux = c( @@ -239,8 +242,11 @@ latexmk_emu = function( stop("Failed to build the index via ", idx_engine, call. = FALSE) }) } - # generate bibliography - bib_engine = match.arg(bib_engine) + # generate bibliography; if bib_engine is not specified, infer it from the aux + # files: biblatex with the biber backend generates a .bcf file, in which case + # we use biber, otherwise bibtex + if (is.null(bib_engine)) bib_engine = if (file.exists(aux_files['bcf'])) 'biber' else 'bibtex' + bib_engine = match.arg(bib_engine, c('bibtex', 'biber')) install_cmd(bib_engine) pkgs_last = character() aux = aux_files[if ((biber <- bib_engine == 'biber')) 'bcf' else 'aux'] diff --git a/man/latexmk.Rd b/man/latexmk.Rd index d75f49f08..9f1a357f5 100644 --- a/man/latexmk.Rd +++ b/man/latexmk.Rd @@ -10,7 +10,7 @@ latexmk( file, engine = c("pdflatex", "xelatex", "lualatex", "latex", "tectonic"), - bib_engine = c("bibtex", "biber"), + bib_engine = NULL, engine_args = NULL, emulation = TRUE, min_times = 1, @@ -32,8 +32,11 @@ lualatex(...) \item{engine}{A LaTeX engine (can be set in the global option \code{tinytex.engine}, e.g., \code{options(tinytex.engine = 'xelatex')}).} -\item{bib_engine}{A bibliography engine (can be set in the global option -\code{tinytex.bib_engine}).} +\item{bib_engine}{A bibliography engine, either \code{'bibtex'} or \code{'biber'} (can +be set in the global option \code{tinytex.bib_engine}). By default (\code{NULL}), the +engine is inferred from the auxiliary files generated during compilation: +if a \code{.bcf} file is found (produced by \pkg{biblatex} with the \code{biber} +backend), \code{'biber'} is used; otherwise \code{'bibtex'} is used.} \item{engine_args}{Command-line arguments to be passed to \code{engine} (can be set in the global option \code{tinytex.engine_args}, e.g., diff --git a/tests/test-ci/bib/biber.tex b/tests/test-ci/bib/biber.tex new file mode 100644 index 000000000..15489e74f --- /dev/null +++ b/tests/test-ci/bib/biber.tex @@ -0,0 +1,7 @@ +\documentclass{article} +\usepackage[backend=biber]{biblatex} +\addbibresource{refs.bib} +\begin{document} +\cite{R-base} +\printbibliography +\end{document} diff --git a/tests/test-ci/bib/refs.bib b/tests/test-ci/bib/refs.bib new file mode 100644 index 000000000..48749e264 --- /dev/null +++ b/tests/test-ci/bib/refs.bib @@ -0,0 +1,8 @@ +@Manual{R-base, + title = {R: A Language and Environment for Statistical Computing}, + author = {{R Core Team}}, + organization = {R Foundation for Statistical Computing}, + address = {Vienna, Austria}, + year = {2016}, + url = {https://www.R-project.org/}, +} diff --git a/tests/test-ci/test-latex.R b/tests/test-ci/test-latex.R index 3b001c64e..77af7e678 100644 --- a/tests/test-ci/test-latex.R +++ b/tests/test-ci/test-latex.R @@ -12,3 +12,14 @@ assert('latexmk() generates the PDF output to the dir of the .tex file by defaul # can also specify a custom pdf output path (latexmk('sub/test.tex', pdf_file = 'foo.pdf') %==% 'foo.pdf') }) + +assert('latexmk() infers biber from the .bcf file when bib_engine is not set', { + # a biblatex + biber document does not generate the \bibdata/\bibstyle info in + # the .aux file that bibtex needs, so bibtex would silently do nothing; the + # bibliography is only built if biber is (correctly) inferred from the .bcf + owd = setwd('bib'); on.exit(setwd(owd), add = TRUE) + latexmk('biber.tex', clean = FALSE) + bbl = readLines('biber.bbl', warn = FALSE) + file.remove(list.files('.', '^biber[.](aux|bbl|bcf|blg|log|pdf|run[.]xml)$')) + (any(grepl('R Core Team', bbl, fixed = TRUE))) +}) From b0e71ff8c85b3875ff7a046926016ebd224a06d3 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Wed, 9 Sep 2026 09:12:30 -0500 Subject: [PATCH 3/3] Bump version number to 0.60.2 [ci skip] --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index c720fb293..ff6e53d3f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: tinytex Type: Package Title: Helper Functions to Install and Maintain TeX Live, and Compile LaTeX Documents -Version: 0.60.1 +Version: 0.60.2 Authors@R: c( person("Yihui", "Xie", role = c("aut", "cre", "cph"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")), person(given = "Posit Software, PBC", role = c("cph", "fnd")),