为了在远程 CI 系统上获得可重现且可靠的文档构建,我希望自动化生成完整文档所需的所有命令行工具和多个编译。无论文档中实际使用了哪些插件,此管道都应该可以立即用于任何文档。
据我了解,有一种方法可以让 LaTeX 构建自动执行所有必要的步骤,但到目前为止,我还没有成功使用任何方法。
细节
我当前的设置是这样的:
- 文档是在我的计算机上使用 LuaLaTeX 和 MiKTeX 编写和编译的
- CI 由 MiKTeX Docker 镜像组成(看这里)已安装所有自定义字体和资源
在本地,我必须按顺序执行各种操作来编译文档,而作为文档的作者,我必须知道这些操作。但 CI 应该能够构建任何文档,而无需应用特殊的每个文档设置,并且在文档无法正确编译或缺少某些内容时产生错误。
例如:词汇表
当我需要构建一个使用该glossaries
包的文档时,本地构建将如下所示
lualatex doc.tex
makeglossaries doc
lualatex doc.tex
现在 CI 不知道文档是否需要此步骤,但仍应能够构建文档。在词汇表文档中提到了参数automake
和--shell-escape
编译文档的选项,我以为这会允许我一次编译所有内容,但这在 CI 上不起作用。同样,我在目录、索引、参考书目等方面也遇到了同样的问题。
概括
有没有一种通用的方法可以自动化不同软件包的复杂编译?(内置于 LuaLaTeX 或一些自定义的通用脚本)
我的设置是否存在问题,导致所有步骤无法自动进行?
我查阅了大量特定软件包或 LaTeX 的资源,但还是找不到有用的。非常感谢您的帮助。
答案1
正如其他人和你自己提到的,latexmk
似乎是这项工作的正确工具。为了了解如何实现它,让我从我自己的 LaTeX 文档/模板 CI 管道(在 GitLab 上)中突出显示相关部分。这应该是“针对不同包的复杂编译”的一个可用示例。我将介绍随附的Dockerfile
和GitLab CI 配置除了 LaTeX 特定的/部分之外latexmk
,因为所有部分都紧密耦合。
下面详细介绍的所有内容都已实现并且(希望......)有效在这个项目中。我试图让这个答案尽可能独立。项目链接将包含最新状态,最终将取代这个答案。
LaTeX 和分发(Debian)包
在链接的管道中,有几个包需要在设置中特别注意。您不太可能有完全相同的要求,但为了完整性,我在这里列出它们。
glossaries-extra
,建立在之上glossaries
,需要bib2gls
转换并处理要使用的*.bib
文件。lualatex
这在设置中体现为双重:
- Docker 镜像需要 Java 运行时环境
bib2gls
, latexmk
需要被告知bib2gls
文件的存在。
- Docker 镜像需要 Java 运行时环境
pgfplots
其contour
选项为\addplot3
(例子)需要外部gnuplot
计划。同样,这体现在两个方面:lualatex
(或者您选择的引擎)需要外部写访问权限才能gnuplot
将其计算结果写入文件以供pgfplots
读取:--shell-escape
需要,必须latexmk
告知。gnuplot
,因为分发包(而不是 LaTeX 包)必须存在,例如apt-get install gnuplot
在 Debian 主机上。
LaTeX 本身无法嵌入
*.svg
文件。嵌入此类文件需要先转换为 PDF(或其他可嵌入格式)。这可以使用Inkscape和它的*.pdf_tex
常规。但是,这导致每个 SVG 文件有两个额外的文件:*.pdf
和*.pdf_tex
。SVG 文件中的每次更改都必须更新派生文件。这会导致潜在的冲突,在版本控制(git
等)的背景下也是如此:要保留哪些版本?这
svg
LaTeX 软件包通过自动化转换过程解决了这些问题。生成的*.pdf
和*.pdf_tex
文件可以作为临时/派生文件处理并自由丢弃。只有 SVG 保留下来,作为单一事实来源. 作为奖励,基于文本 (XML),它们也适用于 VCSgit
(二进制 PDF 实际上不适合)。与以前一样,这在下面的两个地方的设置中反映出来:
- 为了调用 CLI
inkscape
(而不是 GUI;要使其工作,inkscape
必须在您的 上$PATH
)进行读写,LaTeX 引擎需要--shell-escape
。 inkscape
需要在构建环境(Docker 镜像)中可用。
- 为了调用 CLI
使用
tcolorbox
及其\newtcolorbox
命令,我创建了一个新的环境例子:\newtcolorbox[% auto counter,% number within=chapter,% % Set cleveref, see https://tex.stackexchange.com/a/126023/120853: crefname={Example}{Examples}, % List of Examples. *.loe file ending could clash with package thmtools, % careful if that is used! list inside=loe, ]{example}% Name of environment itself [2]% Number of arguments for the environment []% Default of optional argument, which is the first one. Use it for label {% beforeafter skip=18pt plus 4pt minus 4pt,% width=0.95\linewidth,% % Center box; see https://tex.stackexchange.com/a/273111/120853: enlarge left by=0.025\linewidth, title=Example\ \thetcbcounter: #2,% fonttitle=\sffamily,% leftrule=1mm,% arc is angular,% parbox,% Allows regular paragraph breaks breakable,% Breaks across pages enhanced,% Hands drawing to tikz rightrule=0mm,% bottomrule=0mm,% % Setting what ends up in 'list of' so that '<Title>' is not shown: list text=#2, #1,% colback=black!05,% colframe=black!70,% % float, }%
这看起来像:
这是相关的,因为它与
komascript
使用list inside=loe
指令,使我们能够获取并打印示例列表,就像标准图片列表:% Declare a new list of contents, with the file suffix in brackets. % This will give access to \listof<name>s \DeclareNewTOC[% type=example,% This also creates types=example+s, that is by appending an s % Listname is "List of <Type>s" by default % listname={...}, ]{loe}
最后,
latexmk
需要告知这个新创建的*.loe
文件。这很重要,因为latexmk
它通过检查辅助文件的变化来衡量编译进度和完成情况。与上一点类似,使用
listings
包将允许我们打印列表列表从生成的*.lol
文件中获取。和以前一样,latexmk
会想了解它。
最后,生成以下.latexmkrc
文件:
# Contents of .latexmkrc
# PERL latexmk config file
# PDF-generating modes are:
# 1: pdflatex, as specified by $pdflatex variable (still largely in use)
# 2: postscript conversion, as specified by the $ps2pdf variable (useless)
# 3: dvi conversion, as specified by the $dvipdf variable (useless)
# 4: lualatex, as specified by the $lualatex variable (best)
# 5: xelatex, as specified by the $xelatex variable (second best)
$pdf_mode = 4;
# --shell-escape option (execution of code outside of latex) is required for the
#'svg' package.
# It converts raw SVG files to the PDF+PDF_TEX combo using InkScape.
$lualatex = "lualatex --shell-escape";
# option 2 is same as 1 (run biber when necessary), but also deletes the
# regeneratable bbl-file in a clenaup (`latexmk -c`). Do not use if original
# bib file is not available!
$bibtex_use = 2; # default: 1
# Let latexmk know about generated files, so they can be used to detect if a
# rerun is required, or be deleted in a cleanup.
# loe: List of Examples (KOMAScript)
# lol: List of Listings (listings package)
push @generated_exts, 'loe', 'lol';
# Also delete the *.glstex files from package glossaries-extra. Problem is,
# that that package generates files of the form "basename-digit.glstex" if
# multiple glossaries are present. Latexmk looks for "basename.glstex" and so
# does not find those. For that purpose, use wildcard.
# Also delete files generated by gnuplot/pgfplots contour plots
# (.dat, .script, .table),
# and XML file generated by biber runs.
$clean_ext = "%R-*.glstex %R_contourtmp*.* %R.run.xml";
# Grabbed from latexmk CTAN distribution:
# Implementing glossary with bib2gls and glossaries-extra, with the
# log file (.glg) analyzed to get dependence on a .bib file.
# !!! ONLY WORKS WITH VERSION 4.54 or higher of latexmk
# Push new file endings into list holding those files
# that are kept and later used again (like idx, bbl, ...):
push @generated_exts, 'glstex', 'glg';
# Add custom dependency.
# latexmk checks whether a file with ending as given in the 2nd
# argument exists ('toextension'). If yes, check if file with
# ending as in first argument ('fromextension') exists. If yes,
# run subroutine as given in fourth argument.
# Third argument is whether file MUST exist. If 0, no action taken.
add_cus_dep('aux', 'glstex', 0, 'run_bib2gls');
# PERL subroutine. $_[0] is the argument (filename in this case).
# File from author from here: https://tex.stackexchange.com/a/401979/120853
sub run_bib2gls {
if ( $silent ) {
# my $ret = system "bib2gls --silent --group '$_[0]'"; # Original version
my $ret = system "bib2gls --silent --group $_[0]"; # Runs in PowerShell
} else {
# my $ret = system "bib2gls --group '$_[0]'"; # Original version
my $ret = system "bib2gls --group $_[0]"; # Runs in PowerShell
};
my ($base, $path) = fileparse( $_[0] );
if ($path && -e "$base.glstex") {
rename "$base.glstex", "$path$base.glstex";
}
# Analyze log file.
local *LOG;
$LOG = "$_[0].glg";
if (!$ret && -e $LOG) {
open LOG, "<$LOG";
while (<LOG>) {
if (/^Reading (.*\.bib)\s$/) {
rdb_ensure_file( $rule, $1 );
}
}
close LOG;
}
return $ret;
}
latexmk
将选取此文件并自动从中提取配置,如果它被命名为.latexmkrc
。因此,如果该文件存在于 中,则无需明确指定其位置pwd
。
构建环境(Docker 镜像)
最容易获得所需的 Docker 映像的方式是使用debian
基础映像并进行安装texlive-full
(以及上述任何所需软件包,或您需要的任何软件包)。这可以像下面这样简单Dockerfile
:
FROM debian:testing
RUN apt-get update --yes \
&& apt-get install --yes --no-install-recommends \
texlive-full
为了我自己的需要,我准备了更加复杂的 Dockerfile(由于字数限制,评论被删除):
ARG BASE_OS
ARG OS_VERSION
FROM ${BASE_OS}:${OS_VERSION} as BASE
RUN apt-get update && \
apt-get install --yes --no-install-recommends \
wget \
ca-certificates \
perl
FROM BASE as PREPARE
ARG TL_VERSION
ARG TL_INSTALL_ARCHIVE="install-tl-unx.tar.gz"
ARG EISVOGEL_ARCHIVE="Eisvogel.tar.gz"
ARG INSTALL_TL_DIR="install-tl"
COPY texlive.sh .
RUN \
./texlive.sh get ${TL_VERSION} && \
wget https://github.com/Wandmalfarbe/pandoc-latex-template/releases/latest/download/${EISVOGEL_ARCHIVE}
RUN \
mkdir ${INSTALL_TL_DIR} && \
tar --extract --file=${TL_INSTALL_ARCHIVE} --directory=${INSTALL_TL_DIR} --strip-components 1 && \
\
tar --extract --file=${EISVOGEL_ARCHIVE}
FROM BASE as MAIN
ARG BUILD_DATE="n/a"
ARG VCS_REF="n/a"
ARG TL_VERSION
ARG TL_PROFILE="texlive.profile"
LABEL \
maintainer="Alex Povel <[email protected]>" \
org.label-schema.build-date=${BUILD_DATE} \
org.label-schema.description="TeXLive with most packages, JavaRE, Inkscape, pandoc and more" \
org.label-schema.url="https://collaborating.tuhh.de/alex/latex-git-cookbook" \
org.label-schema.vcs-url="https://github.com/alexpovel/latex-extras-docker" \
org.label-schema.vcs-ref=${VCS_REF} \
org.label-schema.schema-version="1.0"
ARG INSTALL_DIR="/install/"
WORKDIR ${INSTALL_DIR}
COPY ${TL_PROFILE} .
COPY --from=PREPARE /install-tl/ /texlive.sh ./
COPY --from=PREPARE /eisvogel.tex /usr/share/pandoc/data/templates/eisvogel.latex
ARG TEXLIVE_INSTALL_PREFIX="/usr/local/texlive"
ARG TEXLIVE_INSTALL_TEXDIR="${TEXLIVE_INSTALL_PREFIX}/${TL_VERSION}"
ARG TEXLIVE_INSTALL_TEXMFCONFIG="~/.texlive${TL_VERSION}/texmf-config"
ARG TEXLIVE_INSTALL_TEXMFVAR="~/.texlive${TL_VERSION}/texmf-var"
ARG TEXLIVE_INSTALL_TEXMFHOME="~/texmf"
ARG TEXLIVE_INSTALL_TEXMFLOCAL="${TEXLIVE_INSTALL_PREFIX}/texmf-local"
ARG TEXLIVE_INSTALL_TEXMFSYSCONFIG="${TEXLIVE_INSTALL_TEXDIR}/texmf-config"
ARG TEXLIVE_INSTALL_TEXMFSYSVAR="${TEXLIVE_INSTALL_TEXDIR}/texmf-var"
RUN ./texlive.sh install ${TL_VERSION}
RUN luaotfload-tool --update || echo "luaotfload-tool not found, skipping."
RUN apt-get update && \
apt-get install --yes --no-install-recommends \
default-jre-headless \
inkscape \
gnuplot-nox \
ghostscript
RUN apt-get update && \
apt-get install --yes --no-install-recommends \
librsvg2-bin \
pandoc
WORKDIR /tex/
RUN rm --recursive ${INSTALL_DIR}
CMD [ "--lualatex" ]
ENTRYPOINT [ "latexmk" ]
它允许用户指定要构建的 TeXLive(从其档案中提取)和 Debian 版本。为此,它需要以下texlive.sh
脚本。它在latest
(Docker)标签和一些历史版本(例如 Debian 9、TeXLive 2018)之间进行选择,在这种情况下,它会从TUG 档案:
#!/bin/bash
# Script to fetch `install-tl` script from different sources, depending on argument
# given.
# Error out of any of the variables used here are unbound, e.g. no CLI arg given.
set -u
usage() {
echo "Usage: $0 get|install latest|version (YYYY)"
}
if [[ $# != 2 ]]; then
echoerr "Unsuitable number of arguments given."
usage
# From /usr/include/sysexits.h
exit 64
fi
# From: https://stackoverflow.com/a/2990533/11477374
echoerr() { echo "$@" 1>&2; }
# Bind CLI arguments to explicit names:
ACTION=${1}
VERSION=${2}
# Download the `install-tl` script from the `tlnet-final` subdirectory, NOT
# from the parent directory. The latter contains an outdated, non-final `install-tl`
# script, causing this exact problem:
# https://tug.org/pipermail/tex-live/2017-June/040376.html
HISTORIC_URL="ftp://tug.org/historic/systems/texlive/${VERSION}/tlnet-final"
REGULAR_URL="http://mirror.ctan.org/systems/texlive/tlnet"
case ${ACTION} in
"get")
if [[ ${VERSION} == "latest" ]]
then
# Get from default, current repository
wget ${REGULAR_URL}/${TL_INSTALL_ARCHIVE}
else
# Get from historic repository
wget ${HISTORIC_URL}/${TL_INSTALL_ARCHIVE}
fi
;;
"install")
if [[ ${VERSION} == "latest" ]]
then
# Install using default, current repository
perl install-tl \
--profile=${TL_PROFILE}
else
# Install using historic repository (`install-tl` script and repository
# versions need to match)
perl install-tl \
--profile=${TL_PROFILE} \
--repository=${HISTORIC_URL}
fi
# For `command` usage, see:
# https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#Bash-Builtins.
# The following test assumes the most basic program, `tex`, is present.
if command -v tex &> /dev/null
then
# If automatic `install-tl` process has already adjusted PATH, we are happy.
echo "PATH and installation seem OK."
else
# Try and make installation available on path manually.
#
# The first wildcard expands to the architecture (should be 'x86_64-linux',
# which might change in TeXLive upstream, so do not hardcode here),
# the second one expands to all binaries found in that directory.
# Only link if directory exists, else we end up with a junk symlink.
EXPECTED_INSTALL_TEXDIR=${TEXLIVE_INSTALL_TEXDIR}/bin/*
# `ls` found to be more robust than `[ -d ... ]`.
if ls ${EXPECTED_INSTALL_TEXDIR} 1>/dev/null 2>&1
then
SYMLINK_DESTINATION="/usr/local/bin"
# "String contains", see: https://stackoverflow.com/a/229606/11477374
if [[ ! ${PATH} == *${SYMLINK_DESTINATION}* ]]
then
# Should never get here, but make sure.
echoerr "Symlink destination ${SYMLINK_DESTINATION} not in PATH (${PATH}), exiting."
exit 1
fi
echo "Symlinking TeXLive binaries in ${EXPECTED_INSTALL_TEXDIR}"
echo "to a directory (${SYMLINK_DESTINATION}) found on PATH (${PATH})"
# Notice the wildcard:
ln --symbolic --verbose ${EXPECTED_INSTALL_TEXDIR}/* ${SYMLINK_DESTINATION}
if command -v tex &> /dev/null
then
echo "PATH and installation seem OK."
else
echoerr "Manual symlinking failed and TeXLive did not modify PATH automatically."
echoerr "Exiting."
exit 1
fi
else
echoerr "Expected TeXLive installation dir not found and TeXLive installation did not modify PATH automatically."
echoerr "Exiting."
exit 1
fi
fi
;;
*)
echoerr "Input not understood."
usage
# From /usr/include/sysexits.h
exit 64
esac
此外,TeXLive 的安装是使用其install-tl
脚本。对于无人值守安装,它需要轮廓文件,如下所示texlive.profile
(由于字符限制,评论被删除):
selected_scheme scheme-custom
collection-basic 1
collection-bibtexextra 1
collection-binextra 1
collection-fontsextra 1
collection-fontsrecommended 1
collection-fontutils 1
collection-formatsextra 1
collection-langenglish 1
collection-langeuropean 1
collection-langgerman 1
collection-latex 1
collection-latexextra 1
collection-latexrecommended 1
collection-luatex 1
collection-mathscience 1
collection-pictures 1
collection-plaingeneric 1
collection-publishers 1
collection-xetex 1
collection-context 0
collection-games 0
collection-humanities 0
collection-langarabic 0
collection-langchinese 0
collection-langcjk 0
collection-langcyrillic 0
collection-langczechslovak 0
collection-langfrench 0
collection-langgreek 0
collection-langitalian 0
collection-langjapanese 0
collection-langkorean 0
collection-langother 0
collection-langpolish 0
collection-langportuguese 0
collection-langspanish 0
collection-metapost 0
collection-music 0
collection-pstricks 0
collection-texworks 0
collection-wintools 0
instopt_adjustpath 1
instopt_adjustrepo 0
instopt_letter 0
instopt_portable 0
instopt_write18_restricted 1
tlpdbopt_autobackup 0
tlpdbopt_backupdir tlpkg/backups
tlpdbopt_create_formats 1
tlpdbopt_desktop_integration 0
tlpdbopt_file_assocs 0
tlpdbopt_generate_updmap 0
tlpdbopt_install_docfiles 0
tlpdbopt_install_srcfiles 0
tlpdbopt_post_code 1
如果您愿意,此文件是映像构建过程的核心。它指定要下载和安装哪些 LaTeX 包。您可以在此处修改您的构建,也许最重要的是精简构建。例如,此处明确省略了安装/下载文档文件,这是在简单运行 时无法实现的apt-get install texlive-full
,从而节省了数 GB 的空间。
请注意,这些图像已经构建并可用(以持续集成的方式:git push
源存储库上的每个图像都将触发构建)DockerHub。使用这些将获得与自己构建相同的图像,不会对 TUG 档案服务器造成压力。这些镜像是使用 DockerHub 的构建钩子,其中设置页面看起来有点像(另请参阅这里):
CI 配置
这是 GitLab 特有的。我还没有为 GitHub/Travis 实现此功能。
在给定的存储库中,*.tex
根目录中有一个或多个文件以及README.md
,下面的 CI YAML 配置(由于字符限制没有注释)将:
- 从上面的 DockerHub 存储库中拉取镜像,将所有
ENTRYPOINT
指令替换为空(即普通 shell)。这对于script
部件的正常工作很重要,而这ENTRYPOINT
对于在桌面上运行容器很方便。 n.a.
将 LaTeX 类文件(位于项目根目录中)中的in\newcommand*{\GitVersion}{n.a.}
和\newcommand*{\GitShortHash}{n.a.}
from替换*.cls
为该构建的实际当前值。这允许将 VCS 元数据打印在 PDF 中。- 只需发出 即可将 LaTeX 构建为 PDF
latexmk
。它将从 中提取说明.latexmkrc
,见上文。 README.md
使用从 编译 PDFpandoc
,再次使用lualatex
进行转换。这是使用模板来获得更漂亮的输出。此步骤更像是 的噱头/展示pandoc
。
生成的 PDF 为文物CI 管道,运行成功后即可下载。
default:
image:
name: alexpovel/latex
entrypoint: [ "" ]
retry:
max: 1
when: runner_system_failure
artifacts:
name: "$CI_COMMIT_REF_NAME"
paths:
- "*.pdf"
stages:
- prepare
- build
insert_git_metadata:
stage: prepare
script:
- |
declare -A GITINFO=(
[GitVersion]=$CI_COMMIT_REF_NAME
[GitShortHash]=$CI_COMMIT_SHORT_SHA
)
- |
for k in "${!GITINFO[@]}"
do
sed -i "s~\(newcommand\*{\\\\$k}\){.*}~\1{${GITINFO[$k]}}~" *.cls
done
artifacts:
paths:
- "*.cls"
needs: []
build_latex:
stage: build
script:
- latexmk
dependencies:
- insert_git_metadata
build_pandoc:
stage: build
script:
- 'sed -i "s~\(^date: \)\".*\"~\1\"$(date +"%B %-d, %Y")\"~" README.md'
- |
pandoc README.md \
--template eisvogel --pdf-engine=lualatex --number-sections \
-o README.pdf
needs: []
答案2
抱歉,我不知道 CI 代表什么,但如果您的机器上有 GNU make,您可以编写一个如下的小型 makefile:
FILE=yourfilename
.PHONY: clean cleanall
all:$(FILE).pdf
clean: -rm *.aux *.blg *.out *.bbl *.lot *.lof *.glo *.ist *.acn *.acr *.alg *.glg *.gls *.toc *.bcf *.run.xml
cleanall: -rm *.aux *.blg *.out *.bbl *.log *.lot *.lof *.glo *.ist *.acn *.acr *.alg *.glg *.gls *.toc *.bcf *.run.xml #report#.pdf
$(FILE).pdf: *.tex
E:\miktex-portable\texmfs\install\miktex\bin\x64\lualatex.exe $(FILE)
E:\miktex-portable\texmfs\install\miktex\bin\x64\biber.exe $(FILE)
E:\miktex-portable\texmfs\install\miktex\bin\x64\makeglossaries.exe $(FILE)
E:\miktex-portable\texmfs\install\miktex\bin\x64\lualatex.exe $(FILE)
E:\miktex-portable\texmfs\install\miktex\bin\x64\lualatex.exe $(FILE)
该示例针对 Windows 操作系统。不要使用 E:\miktex-portable...\,请调整适合您安装的路径。如果您使用的是 GNU/Linux,则可以设置 $PATH,这样就不需要指定可执行文件的完整路径(即只需说“lualatex $(FILE)”等)。据了解,参考列表是使用 biber 和 biblatex 创建的。makefile(相同文件名)应放在 *.tex 文件所在的同一目录中。然后,您只需在命令行终端中输入“make”。同样,“make clean”和“make cleanall”可以帮助您整理目录。
答案3
我最终做的是申请latexmk
CI 并应用额外的规则来latexmk
帮助打包glossaries
。
设置构建环境和 miktex docker 镜像后,自动构建执行以下操作:
mpm --install=latexmk
构建文档时使用以下命令:
latexmk -r "<path-to-rc-file>/.latexmkrc" -lualatex -latexoption="-interaction=nonstopmode"
该.latexmkrc
文件位于我们所有文档存储库之间共享的 Git 子模块中,我们在其中还有共享的文档类等。
以下是.latexmkrc
# This shows how to use lualatex (http://en.wikipedia.org/wiki/LuaTeX)
# with latexmk.
#
# WARNING: The method shown here is suitable only for ver. 4.51 and
# later of latexmk, not for earlier versions.
#
$pdf_mode = 4;
$postscript_mode = $dvi_mode = 0;
# This shows how to use the glossaries package
# (http://www.ctan.org/pkg/glossaries) and the glossaries-extra package
# (http://www.ctan.org/pkg/glossaries-extra) with latexmk.
add_cus_dep( 'acn', 'acr', 0, 'makeglossaries' );
add_cus_dep( 'glo', 'gls', 0, 'makeglossaries' );
$clean_ext .= " acr acn alg glo gls glg";
sub makeglossaries {
my ($base_name, $path) = fileparse( $_[0] );
pushd $path;
my $return = system "makeglossaries", $base_name;
popd;
return $return;
}
取自这里。
无论文档及其内容如何,最终结果都能确保一致的构建体验。这种方法还具有灵活性和可扩展性,可以在编写时使用来自本地 Docker 容器的完全相同的工具链。