> For the complete documentation index, see [llms.txt](https://overleaf-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://overleaf-pro.ayaka.space/latex/zh-tw/zhi-shi-ku/064-how-does-overleaf-compile-my-project.md).

# Overleaf 如何編譯我的專案？

## 自動化 LaTeX 編譯

為了支援目錄、圖表清單、交叉參照、詞彙表、索引以及書目引用等功能，LaTeX 使用多重執行的排版流程：在一次執行（編譯）中產生的資料會寫入檔案，並作為之後可能需要的任何後續執行的輸入。以下影片顯示你每次在 Overleaf 專案中按下「Recompile」按鈕時會發生什麼事：

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

在手動編譯時，通常很明顯是否需要再執行一次 LaTeX（一次 pass），但自動化 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 特定處理的程式碼，以及 glossary、nomenclature 與其他常用套件的規則。Overleaf 的 `LatexMk` 檔案在不同 TeX Live 版本之間可能會有些變動，以因應 TeX Live 本身的套件與軟體工具所做的變更與更新。

### 變更 LaTeX 的編譯模式

預設情況下，Overleaf 會指示 latexmk 使用一種編譯模式，我們（Overleaf）稱之為 **盡量在有錯誤時仍繼續編譯**。在該編譯模式下，LaTeX 編譯器即使遇到 [LaTeX 編譯錯誤](/latex/zh-tw/latex-ji-chu/05-errors.md)也不會停止；即使輸出有誤，仍可能產生 PDF。通常，編譯錯誤可能過於嚴重，或數量太多，以致 LaTeX 編譯器無法修正，因此它仍可能在沒有產生 PDF 的情況下「中止」。

你可以將 LaTeX 編譯模式改為在遇到第一個錯誤時立即停止編譯：請參閱說明文章 [使用「遇到第一個錯誤即停止」編譯模式](/latex/zh-tw/zhi-shi-ku/149-using-the-stop-on-first-error-compilation-mode.md) 以取得更多資訊。

### 如何取得 Overleaf 的 LatexMk 檔案副本

以下連結會開啟一個專案，以排版 Overleaf `LatexMk` 專案中所使用的檔案與 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*{列出 \texttt{LatexMk} 檔案}
\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/49031319daac91a696e7265f370d920c09c14512)

如果你想使用 latexmk 在離線狀態下建置你的 Overleaf 專案，你可能會想使用 Overleaf 的 LatexMk 檔案副本，以確保它套用 Overleaf 的建置流程——請注意 Overleaf 的 job name 設為 output。

Overleaf 使用 latexmk 的 `-cd` 命令列旗標，且編譯一律從專案的根目錄執行。請注意某些 `latexmk` 選項與 Overleaf 不相容——特別是，如果你設定 `$preview_continuous_mode` 或 `$output_dir` 選項，這可能會破壞 Overleaf 上的預覽。

如需更多關於 `latexmkrc` 檔案的資訊，請閱讀文章 [如何在 Overleaf 中使用 latexmkrc](/latex/zh-tw/shen-ru-wen-zhang/28-how-to-use-latexmkrc-with-overleaf.md) ，其中有我們親切的 Overleaf TeXperts 提供的實用技巧與訣竅。

## Overleaf 的 LatexMk 檔案清單（2023）

為方便起見，下列清單顯示 Overleaf 的 `LatexMk` 檔案，使用的是 TeX Live 2023——請使用上方連結來存取最新版本。

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

# 讓 pstricks 透明度可用的變通方法 (https://github.com/overleaf/issues/issues/3449)
$dvipdf = \"dvipdf -dNOSAFER -dALLOWPSTRANSPARENCY %O %S %D\";

###############################
# PDF 檔後處理 #
###############################

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

# 等同於 -gt 選項。用於防止 latexmk 略過重新編譯
# 的 output.log 與 output.pdf
$go_mode = 3;

# 等同於 -bibtex 選項。當 bbl 檔案
# 過期時無條件執行 bibtex。
$bibtex_use = 2;

my $ORIG_PDF_AGE;

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

    # 若存在，取得現有 pdf 的年齡
    $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;

    # 尋找 knitr concordance 檔案
    my $concordance_file = "${source_without_ext}-concordance.tex";
    if (-e $concordance_file) {
        print "正在修補 knitr 的 synctex 檔案...\n";
        system("patchSynctex.R", $source_without_ext, $output_without_ext);
    }

    # 如果 pdf 檔不存在或未更新，則提早返回
    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;

    # 找出 qpdf 位於何處
    $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});

    # 執行 qpdf
    my $optimised_file = "${output_file}.opt";
    system($qpdf, split(' ', $qpdf_opts), $output_file, $optimised_file);
    $qpdf_exit_code = ($? >> 8);
    print "qpdf 結束代碼=$qpdf_exit_code\n";

    # 如果 qpdf 成功，就取代輸出檔
    # qpdf 成功時回傳 0，警告時回傳 3（仍會建立輸出 pdf）
    return if !($qpdf_exit_code == 0 || $qpdf_exit_code == 3);
    print "將最佳化後的檔案重新命名為 $output_file\n";
    rename($optimised_file, $output_file);

    print "正在為 $output_file 擷取 xref 表...\n";
    my $xref_file = "${output_file}xref";
    system("$qpdf --show-xref ${output_file} > ${xref_file}");
    $qpdf_xref_exit_code = ($? >> 8);
    print "qpdf --show-xref 結束代碼=$qpdf_xref_exit_code\n";
}

##############
# 詞彙表 #
##############
add_cus_dep( 'glo', 'gls', 0, 'glo2gls' );
add_cus_dep( 'acn', 'acr', 0, 'glo2gls');  # 來自 Overleaf v1
sub glo2gls {
    system("makeglossaries $_[0]");
}

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

################
# 名詞表 #
################
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  #  # 來自 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;

    # 識別主檔案
    my $target = $ARGV[-1];
    my $file = basename($target);

    if ($file =~ /\.tex$/) {
        # 在有限範圍內變更目錄
        my $orig_dir = cwd();
        my $subdir = dirname($target);
        chdir($subdir);
        # 在主檔案上執行 chktex
        $status = system("/usr/local/bin/run-chktex.sh", $orig_dir, $file);
        # 回到原始目錄
        chdir($orig_dir);

        # 在 VALIDATE 模式下，我們會在執行 chktex 後一律結束
        # 否則，如果 EXIT_ON_ERROR 已設定，我們就會結束

        if ($ENV{'CHKTEX_EXIT_ON_ERROR'} || $ENV{'CHKTEX_VALIDATE'}) {
            # chktex 不允許我們透過結束狀態存取錯誤資訊
            # 因此請檢查輸出內容
            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://overleaf-pro.ayaka.space/latex/zh-tw/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.
