> For the complete documentation index, see [llms.txt](https://ayakaleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ayakaleaf-pro.ayaka.space/latex/zh-cn/zhi-shi-ku/064-how-does-overleaf-compile-my-project.md).

# Overleaf 是如何编译我的项目的？

## 自动化 LaTeX 编译

为了支持目录、图表清单、交叉引用、术语表、索引和参考文献引用等功能，LaTeX 采用多遍排版流程：在一次排版（编译）中生成的数据会写入文件，并作为后续所需任何一次排版的输入。下面的视频展示了你每次在 Overleaf 项目中点击“重新编译”按钮时会发生什么：

{% embed url="<https://videos.ctfassets.net/nrgyaltdicpt/Uo3I2FXEI8H6aFPBZq171/e2139f9575a4db6955dc2278dc08283c/latexmk-ol.mp4>" %}

在手动编译时，通常很容易看出是否需要再运行一次 LaTeX（即一次排版），但自动化 LaTeX 编译却带来许多困难。自动化方案的一个关键挑战是确定 *多少次* LaTeX 排版（编译）需要多少次，以解决任何数据依赖，从而确保最终排版的文档是最新且完整的。另一个复杂之处在于正确识别必须执行的外部程序，以预处理文档内容的某些部分——例如运行 BibTeX/BibLaTeX 生成参考文献列表，或执行更专业的图形应用程序，例如 [MetaPost](https://tug.org/metapost.html) 或 [Asymptote](https://asymptote.sourceforge.io/).

## 介绍 latexmk

一个广为使用的编译自动化包是 [`latexmk`](https://ctan.org/pkg/latexmk?lang=en)，这是一个包含在 TeX Live 中的大型 Perl 脚本，Overleaf 使用它来编译你的 LaTeX 项目。 `latexmk` 它会自动触发所需次数的 LaTeX 运行，并执行处理文档内容部分所需的外部（辅助）程序。

### 定制 latexmk

尽管 `latexmk` 它非常灵活，不同的 LaTeX 编译环境以及极其广泛的文档类型，使得它不太可能适用于 `latexmk` 尝试提供能够完全满足每一种可想象的 LaTeX 文档需求的自动化方案。相反，其行为 `latexmk` 可通过配置文件进行定制 *实现于 **Perl***:

* 一个“系统级”配置文件，用于适配特定的计算环境/平台，以及
* 一个用户级配置文件，用于处理特定文档的需求

该 [`latexmk 手册`](http://mirrors.ctan.org/support/latexmk/latexmk.pdf) 提供了有关安装和使用配置文件的更多细节。

## Overleaf 的 LatexMk 文件

Overleaf 的“系统级”配置文件遵循 `latexmk` 约定（适用于 Linux）：其名称为 `**LatexMk**` 并位于文件夹 `/usr/local/share/latexmk/`. **注意**：该文件会在读取 *在……之前* 任何用户级配置文件之前被读取。

用户级配置文件必须命名为 `latexmkrc` ，并添加到你的 Overleaf 项目中，以提供自定义规则来满足你的 Overleaf LaTeX 项目/文档的特定需求。

Overleaf 的 `LatexMk` 文件包含用于 Overleaf 特定处理的代码，以及术语表、符号表和其他常用宏包的规则。Overleaf 的 `LatexMk` 文件在不同 TeX Live 版本之间可能会略有不同，以适应 TeX Live 本身内宏包和软件工具的变更与更新。

### 更改 LaTeX 的编译模式

默认情况下，Overleaf 指示 latexmk 使用一种编译模式，我们（Overleaf）将其称为 **尽管有错误仍尝试编译**。在该编译模式下，即使遇到 [LaTeX 编译错误](/latex/zh-cn/latex-ji-chu/05-errors.md)；即使输出不正确，也可能生成 PDF。通常，编译错误可能过于严重或过于繁多，LaTeX 编译器无法修复，因此它仍可能“退出”而不生成 PDF。

你可以更改 LaTeX 编译模式，使其在遇到第一个错误时立即停止：请参阅帮助文章 [使用“首次错误即停止”编译模式](/latex/zh-cn/zhi-shi-ku/149-using-the-stop-on-first-error-compilation-mode.md) ，可了解更多信息。

### 如何获取 Overleaf 的 LatexMk 文件副本

以下链接会打开一个项目，用于排版 Overleaf 的 `LatexMk` 文件，该文件与项目的 LaTeX 编译器和 TeX Live 版本一起使用，并可供下载：

```latex
\documentclass[a4paper]{article}
\usepackage[margin=1cm]{geometry}
\usepackage{verbatim,shellesc}
\ShellEscape{cp /usr/local/share/latexmk/LatexMk ./LatexMk}
\begin{document}
\section*{About this project}
本项目提供对 Overleaf 所使用的系统 \texttt{LatexMk} 初始化（配置）文件的访问。  \texttt{LatexMk} 是一个 Perl 脚本，可能会因所选项目的 \TeX{} Live 版本和编译器不同而略有变化。如果你想 100\% 确定当前使用的是哪个  \texttt{LatexMk}，请将本项目中的代码添加到你的项目中并编译，以排版出 \texttt{LatexMk} 的清单，并将其作为输出文件之一供下载。
\section*{Listing the \texttt{LatexMk} file}
\verbatiminput{./LatexMk}
\end{document}
```

[在 Overleaf 中打开此代码。](https://www.overleaf.com/docs?engine=\&snip_name=Display+Overleaf+latexmkrc+file\&snip=%5Cdocumentclass%5Ba4paper%5D%7Barticle%7D%0A%5Cusepackage%5Bmargin%3D1cm%5D%7Bgeometry%7D%0A%5Cusepackage%7Bverbatim%2Cshellesc%7D%0A%5CShellEscape%7Bcp+%2Fusr%2Flocal%2Fshare%2Flatexmk%2FLatexMk+.%2FLatexMk%7D%0A%5Cbegin%7Bdocument%7D%0A%5Csection%2A%7BAbout+this+project%7D%0AThis+project+provides+access+to+the+system+%5Ctexttt%7BLatexMk%7D+initialization+%28configuration%29+file+used+by+Overleaf.++%5Ctexttt%7BLatexMk%7D+is+a+Perl+script+which+may+vary+slightly+according+to+the+%5CTeX%7B%7D+Live+version+and+compiler+chosen+for+the+project.+If+you+need+to+be+100%5C%25+certain+which++%5Ctexttt%7BLatexMk%7D+is+being+used%2C+add+code+from+this+project+to+your+project+and+compile+to+typeset+a+listing+of+%5Ctexttt%7BLatexMk%7D+and+make+it+available+for+download+as+one+of+the+output+files.%0A%5Csection%2A%7BListing+the+%5Ctexttt%7BLatexMk%7D+file%7D%0A%5Cverbatiminput%7B.%2FLatexMk%7D%0A%5Cend%7Bdocument%7D)

编译上面的项目后， `LatexMk` 文件可以通过 **日志和输出文件** 按钮下载，如下方简短视频所示：

{% embed url="<https://videos.ctfassets.net/nrgyaltdicpt/1re6AeMUF2EQnlZ8Cse5ke/ae6ee91fa7e2b2efa8ffb27ad8bab1a0/latexmkrc.mp4>" %}

### 其他说明与建议

如果你想设置或检查用于编译项目的 TeX Live 版本，请参阅这篇 [Overleaf 博客文章](https://www.overleaf.com/blog/new-feature-select-your-tex-live-compiler-version#How-Switch) 或下图：

![选择 Overleaf TeX Live 版本](/files/1d0ec3b69086ea5d7c1c368741f83132507dd923)

如果你想使用 latexmk 在离线状态下构建你的 Overleaf 项目，可能需要使用 Overleaf 的 LatexMk 文件副本，以确保其应用 Overleaf 的构建流程——请注意，Overleaf 的作业名称设置为 output。

Overleaf 使用 latexmk 的 `-cd` 命令行标志，并且编译始终从项目的根目录运行。请注意，某些 `latexmk` 选项与 Overleaf 不兼容——尤其是如果你设置了 `$preview_continuous_mode` 或 `$output_dir` 选项，可能会破坏 Overleaf 上的预览。

有关 `latexmkrc` 文件的更多信息，请阅读文章 [如何在 Overleaf 中使用 latexmkrc](/latex/zh-cn/shen-du-wen-zhang/28-how-to-use-latexmkrc-with-overleaf.md) ，其中包含我们友好的 Overleaf TeXperts 提供的实用技巧与窍门。

## Overleaf 的 LatexMk 文件清单（2023）

为方便起见，下面的清单显示了 Overleaf 的 `LatexMk` 文件在 TeX Live 2023 中的使用版本——请使用上面的链接访问最新版本。

```perl
# Settings
$xdvipdfmx = "xdvipdfmx -z 6 -i dvipdfmx-unsafe.cfg -o %D %O %S";

# Workaround to allow pstricks transparency (https://github.com/overleaf/issues/issues/3449)
$dvipdf = "dvipdf -dNOSAFER -dALLOWPSTRANSPARENCY %O %S %D";

###############################
# Post processing of pdf file #
###############################

$compiling_cmd = "internal overleaf_pre_process %T %D";
$success_cmd = "internal overleaf_post_process %T %D";
$failure_cmd = $success_cmd;

# equivalent to -gt option. Used to prevent latexmk from skipping recompilation
# of output.log and output.pdf
$go_mode = 3;

# equivalent to -bibtex option. Run bibtex unconditionally when bbl files are
# out of date.
$bibtex_use = 2;

my $ORIG_PDF_AGE;

sub overleaf_pre_process {
    my $source_file = $_[0];
    my $output_file = $_[1];

    # get age of existing pdf if present
    $ORIG_PDF_AGE = -M $output_file
}

sub overleaf_post_process {
    my $source_file = $_[0];
    my $output_file = $_[1];
    my $source_without_ext = $source_file =~ s/\.tex$//r;
    my $output_without_ext = $output_file =~ s/\.pdf$//r;

    # Look for a knitr concordance file
    my $concordance_file = "${source_without_ext}-concordance.tex";
    if (-e $concordance_file) {
        print "Patching synctex file for knitr...\n";
        system("patchSynctex.R", $source_without_ext, $output_without_ext);
    }

    # Return early if pdf file doesn't exist or wasn't updated
    my $NEW_PDF_AGE = -M $output_file;
    return if !defined($NEW_PDF_AGE);
    return if defined($ORIG_PDF_AGE) && $NEW_PDF_AGE == $ORIG_PDF_AGE;

    # Figure out where qpdf is
    $qpdf //= "/usr/bin/qpdf";
    $qpdf = $ENV{QPDF} if defined($ENV{QPDF}) && -x $ENV{QPDF};
    return if ! -x $qpdf;
    $qpdf_opts //= "--linearize --newline-before-endstream";
    $qpdf_opts = $ENV{QPDF_OPTS} if defined($ENV{QPDF_OPTS});

    # Run qpdf
    my $optimised_file = "${output_file}.opt";
    system($qpdf, split(' ', $qpdf_opts), $output_file, $optimised_file);
    $qpdf_exit_code = ($? >> 8);
    print "qpdf exit code=$qpdf_exit_code\n";

    # Replace the output file if qpdf was successful
    # qpdf returns 0 for success, 3 for warnings (output pdf still created)
    return if !($qpdf_exit_code == 0 || $qpdf_exit_code == 3);
    print "Renaming optimised file to $output_file\n";
    rename($optimised_file, $output_file);

    print "Extracting xref table for $output_file\n";
    my $xref_file = "${output_file}xref";
    system("$qpdf --show-xref ${output_file} > ${xref_file}");
    $qpdf_xref_exit_code = ($? >> 8);
    print "qpdf --show-xref exit code=$qpdf_xref_exit_code\n";
}

##############
# Glossaries #
##############
add_cus_dep( 'glo', 'gls', 0, 'glo2gls' );
add_cus_dep( 'acn', 'acr', 0, 'glo2gls');  # from Overleaf v1
sub glo2gls {
    system("makeglossaries $_[0]");
}

#############
# makeindex #
#############
@ist = glob("*.ist");
if (scalar(@ist) > 0) {
    $makeindex = "makeindex -s $ist[0] %O -o %D %S";
}

################
# nomenclature #
################
add_cus_dep("nlo", "nls", 0, "nlo2nls");
sub nlo2nls {
        system("makeindex $_[0].nlo -s nomencl.ist -o $_[0].nls -t $_[0].nlg");
}

#########
# Knitr #
#########
add_cus_dep( 'Rtex', 'tex', 0, 'do_knitr');
add_cus_dep( 'Rnw', 'tex', 0, 'do_knitr');
sub do_knitr {
    Run_subst(qq{Rscript -e '
        library("knitr");
        opts_knit\$set(concordance=T);
        knitr::knit(%S, output=%D);
        '}
    );
}

##########
# feynmf #
##########
push(@file_not_found, '^feynmf: Files .* and (.*) not found:$');
add_cus_dep("mf", "tfm", 0, "mf_to_tfm");
sub mf_to_tfm { system("mf '\\mode:=laserjet; input $_[0]'"); }

push(@file_not_found, '^feynmf: Label file (.*) not found:$');
add_cus_dep("mf", "t1", 0, "mf_to_label1");
sub mf_to_label1 { system("mf '\\mode:=laserjet; input $_[0]' && touch $_[0].t1"); }
add_cus_dep("mf", "t2", 0, "mf_to_label2");
sub mf_to_label2 { system("mf '\\mode:=laserjet; input $_[0]' && touch $_[0].t2"); }
add_cus_dep("mf", "t3", 0, "mf_to_label3");
sub mf_to_label3 { system("mf '\\mode:=laserjet; input $_[0]' && touch $_[0].t3"); }
add_cus_dep("mf", "t4", 0, "mf_to_label4");
sub mf_to_label4 { system("mf '\\mode:=laserjet; input $_[0]' && touch $_[0].t4"); }
add_cus_dep("mf", "t5", 0, "mf_to_label5");
sub mf_to_label5 { system("mf '\\mode:=laserjet; input $_[0]' && touch $_[0].t5"); }
add_cus_dep("mf", "t6", 0, "mf_to_label6");
sub mf_to_label6 { system("mf '\\mode:=laserjet; input $_[0]' && touch $_[0].t6"); }
add_cus_dep("mf", "t7", 0, "mf_to_label7");
sub mf_to_label7 { system("mf '\\mode:=laserjet; input $_[0]' && touch $_[0].t7"); }
add_cus_dep("mf", "t8", 0, "mf_to_label8");
sub mf_to_label8 { system("mf '\\mode:=laserjet; input $_[0]' && touch $_[0].t8"); }
add_cus_dep("mf", "t9", 0, "mf_to_label9");
sub mf_to_label9 { system("mf '\\mode:=laserjet; input $_[0]' && touch $_[0].t9"); }

##########
# feynmp #
##########
push(@file_not_found, '^dvipdf: Could not find figure file (.*); continuing.$');
add_cus_dep("mp", "1", 0, "mp_to_eps");
sub mp_to_eps {
    system("mpost $_[0]");
    return 0;
}

#############
# asymptote #
#############

sub asy {return system("asy \"$_[0]\"");}
add_cus_dep("asy","eps",0,"asy");
add_cus_dep("asy","pdf",0,"asy");
add_cus_dep("asy","tex",0,"asy");

#############
# metapost  #  # from Overleaf v1
#############
add_cus_dep('mp', '1', 0, 'mpost');
sub mpost {
    my $file = $_[0];
    my ($name, $path) = fileparse($file);
    pushd($path);
    my $return = system "mpost $name";
    popd();
    return $return;
}

##########
# chktex #
##########
unlink 'output.chktex' if -f 'output.chktex';
if (defined $ENV{'CHKTEX_OPTIONS'}) {
    use File::Basename;
    use Cwd;

    # identify the main file
    my $target = $ARGV[-1];
    my $file = basename($target);

    if ($file =~ /\.tex$/) {
        # change directory for a limited scope
        my $orig_dir = cwd();
        my $subdir = dirname($target);
        chdir($subdir);
        # run chktex on main file
        $status = system("/usr/local/bin/run-chktex.sh", $orig_dir, $file);
        # go back to original directory
        chdir($orig_dir);

        # in VALIDATE mode we always exit after running chktex
        # otherwise we exit if EXIT_ON_ERROR is set

        if ($ENV{'CHKTEX_EXIT_ON_ERROR'} || $ENV{'CHKTEX_VALIDATE'}) {
            # chktex doesn't let us access the error info via exit status
            # so look through the output
            open(my $fh, "<", "output.chktex");
            my $errors = 0;
            {
                local $/ = "\n";
                while(<$fh>) {
                    if (/^\S+:\d+:\d+: Error:/) {
                        $errors++;
                        print;
                    }
                }
            }
            close($fh);
            exit(1) if $errors > 0;
            exit(0) if $ENV{'CHKTEX_VALIDATE'};
        }
    }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ayakaleaf-pro.ayaka.space/latex/zh-cn/zhi-shi-ku/064-how-does-overleaf-compile-my-project.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
