pyてよn日記

一寸先は闇が人生

VSCode + LaTeX 環境構築の試行錯誤の記録 at Xmas

本記事の内容は古い、かつ非効率です。本記事の内容は全て無視して、以下の GitHub リポジトリの README を見ながら LaTeX 環境を構築してみてください。Docker + LaTeX + VSCodeLaTeX 執筆環境を構築しています。

github.com

ローカルに LaTeX 環境を作ろうとするとだいたいハマるので、再現性のある環境構築ができる Docker で環境構築を行うべきだと思います。


(以下、内容の古い、非効率な内容となっております)

 殴り書き.エラー処理を順に追って LaTeX 環境を VSCode に構築していく泥臭い記事.クリスマスと言えば LaTeX の環境構築ですよね?

背景

 ラップトップを Macbook Air から Macbook Pro に乗り換えたため,新たに LaTeX 環境を構築する必要があった.以前環境構築した際は,再現性を全く考えておらず,エラー処理の過程で設定を書き足したり,ローカルに色んなパッケージを入れまくったりでカオスになってしまったため,その環境に何も触れない状態になってしまった.

 本記事ではその反省を生かし,実行したコマンド,その最中で発生したエラーの処理など,行ったこと全てを記録することにした.あくまでも記録であり再現性は保証しない.

概要

 以下の記事を進め,VSCode 上に LaTeX 環境を構築する.

 本記事のゴールは「VSCode 上で *.tex ファイルを編集,保存したら PDF が自動生成されてプレビューが速やかに更新される」ことである.

開発環境

# macOS
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.2

# Homebrew
$ brew -v
Homebrew 2.2.2
Homebrew/homebrew-core (git revision 8fae; last commit 2019-12-23)
Homebrew/homebrew-cask (git revision 783b1; last commit 2019-12-23)

# VSCode
$ code --version
1.41.1

# VSCode の LaTeX 関連の extension
$ code --list-extensions | xargs -L 1 echo code --install-extension | grep "latex" --color
code --install-extension James-Yu.latex-workshop

環境構築の手順

 実行したことを忠実に記録した.

1. brew cask で MacTeX のインストール

 GUI はいらんので -no-gui を付けてる.インストールには 1.5 h くらいかかった(余計なものを入れたくない場合は BasicTeX という最小構成のものがあるが,日本語環境などを追加で設定する必要があるらしく,苦しみたくないので自分は避けた).

$ brew cask install mactex-no-gui

 インストール後,シェルを再起動し,platexlatexmk が実行できればインストールに成功している.

$ platex
This is e-pTeX, Version 3.14159265-p3.8.2-190131-2.6 (utf8.euc) (TeX Live 2019) (preloaded format=platex)
 restricted \write18 enabled.
**^C

$ latexmk
Latexmk: This is Latexmk, John Collins, 17 March 2019, version: 4.63b.

Latexmk: No file name specified, and I couldn't find any
Use
   latexmk -help
to get usage information

 ちゃんとインストールできてるっぽい.他にもパッケージ管理ツール tlmgr なども一緒にインストールされている.何がインストールされているかは以下のコマンドで確認できる.

$ ls /Library/TeX/texbin
# 省略

2. LaTeX のための VSCode 拡張 LaTeX Workshop をインストール

 VSCodeGUI)で LaTeX Workshop インストールした(実際には,以前使用していた Macbook Air での環境を同期していたのでアップデートしただけ).

 また,以下の設定を VSCodesettings.json に追加した(以前の設定は全て消去してから以下を加えた).内容は冒頭の参考記事のコピペ.

...
  // ---------- Language ----------

  "[tex]": {
    // スニペット補完中にも補完を使えるようにする
    "editor.suggest.snippetsPreventQuickSuggestions": false,
    // インデント幅を2にする
    "editor.tabSize": 2
  },

  "[latex]": {
    // スニペット補完中にも補完を使えるようにする
    "editor.suggest.snippetsPreventQuickSuggestions": false,
    // インデント幅を2にする
    "editor.tabSize": 2
  },

  "[bibtex]": {
    // インデント幅を2にする
    "editor.tabSize": 2
  },

  // ---------- LaTeX Workshop ----------

  // 使用パッケージのコマンドや環境の補完を有効にする
  "latex-workshop.intellisense.package.enabled": true,

  // latex のビルドで生成されるファイルの出力先ディレクトリ
  "latex-workshop.latex.outDir": "out",

  // 生成ファイルを削除するときに対象とするファイル
  // デフォルト値に "*.synctex.gz" を追加
  "latex-workshop.latex.clean.fileTypes": [
    "*.aux",
    "*.bbl",
    "*.blg",
    "*.idx",
    "*.ind",
    "*.lof",
    "*.lot",
    "*.out",
    "*.toc",
    "*.acn",
    "*.acr",
    "*.alg",
    "*.glg",
    "*.glo",
    "*.gls",
    "*.ist",
    "*.fls",
    "*.log",
    "*.fdb_latexmk",
    "*.snm",
    "*.nav",
    "*.dvi",
    "*.synctex.gz"
  ],

  // 生成ファイルを "out" ディレクトリに吐き出す
  "latex-workshop.latex.outDir": "out",

  // ビルドのレシピ
  "latex-workshop.latex.recipes": [
    {
      "name": "latexmk",
      "tools": ["latexmk"]
    }
  ],

  // ビルドのレシピに使われるパーツ
  "latex-workshop.latex.tools": [
    {
      "name": "latexmk",
      "command": "latexmk",
      "args": ["-silent", "-outdir=%OUTDIR%", "%DOC%"]
    }
  ]
...

3. .latexmkrc の作成,編集

 自分の dotfiles に下記の内容の .latexmkrc を追加し,symlink を張った.

  • .latexmkrc

 こちらも参考記事のコピペ.

#!/usr/bin/env perl

# LaTeX
$latex = 'platex -synctex=1 -halt-on-error -file-line-error %O %S';
$max_repeat = 5;

# BibTeX
$bibtex = 'pbibtex %O %S';
$biber = 'biber --bblencoding=utf8 -u -U --output_safechars %O %S';

# index
$makeindex = 'mendex %O -o %D %S';

# DVI / PDF
$dvipdf = 'dvipdfmx %O -o %D %S';
$pdf_mode = 1;

# preview
$pvc_view_file_via_temporary = 0;
if ($^O eq 'linux') {
    $dvi_previewer = "xdg-open %S";
    $pdf_previewer = "xdg-open %S";
} elsif ($^O eq 'darwin') {
    $dvi_previewer = "open %S";
    $pdf_previewer = "open %S";
} else {
    $dvi_previewer = "start %S";
    $pdf_previewer = "start %S";
}

# clean up
$clean_full_ext = "%R.synctex.gz"

 設定の内容は下記の通り.

特に変わった設定はしていませんが、コマンドラインから latexmk を実行する場合でも、いい感じに動くように以下のように設定しています。

  • TeX のソースから PDF への変換で platex と dvipdfmx を利用
  • platex でのエラーをわかりやすく
  • 対応しているエディタでは PDF プレビューから対応する TeX ソース箇所にジャンプ可能
  • 簡単に PDF のプレビューを行える
  • latexmk -pv report.tex のようにすることで、ビルド後に PDF を表示
  • latexmk -pvc report.tex のようにすることで、ソースを保存するごとに自動で PDF を更新
  • symlink 時に実行したコマンド

 これは本題とはあまり関係がない.ホームディレクトリに直接 ~/.latexmkrc を置けば良い.

ln -sf $HOME/Projects/dotfiles/latex/.latexmkrc ~/.latexmkrc

4. ビルドしてみる

 VSCode を再起動して(latexmkVSCode を認識させるために必須.正確には VSCodePATH を読み込ませるため.), tex ファイルを編集してみる.

  • 既存の tex ファイルを開く.
  • VSCode のサイドバーの "TEX" ボタンを押すとメニュが開く(以下の画像参照).

vscode-sidebar-tex-menu
VSCode のサイドバーに TeX のアイコンがある

  • VSCode を再起動後ビルドを実行
    • cmd + shift + PMac)でコマンドパレットを表示.
    • LaTeX Workshop: Build with recipe > latexmk を選択(てかこの段階では latexmk という選択肢しかないはず)

補足:再起動で直るエラー

(めっちゃ単純なことをだらだら殴り書きしてます.飛ばしても後ろの手順には影響ないです.)

 VSCode の統合ターミナル(VSCode 内で開けるターミナル)で MacTex をインストール後,VSCode を再起動せずに tex ファイルのビルドを行うと以下のエラーが小さくエディタの下部に表示された(再起動すると直る).

f:id:pytwbf201830:20191224222825p:plain
再起動で治るエラー

  • エラーメッセージ
Recipe terminated fatal error: spawn latexmk ENOENT.
  • エラーメッセージについて調べる

    • spawn:「魚が産卵する」,「生じる」という意味.この文脈では,新しい子プロセスを生成してコマンドを実行するという意味(この単語色んなとこで見る気がする).
    • ENOENTError NO ENTry の略.ファイルやディレクトリが存在しないという意味のエラーメッセージらしい.
  • ログを見る(VSCode のサイドバーの "TEX" マーク > View Log messages > View LaTeX Workshop extension log)

# 深夜 3 時です
[03:00:05] Found root file from active editor: [ファイルのフルパス]
[03:00:05] Root file remains unchanged from: [ファイルのフルパス]
[03:00:05] Building root file: [ファイルのフルパス]
[03:00:05] Build root file [ファイルのフルパス]
[03:00:05] Recipe step 1: latexmk, -silent,-outdir=out,[ファイルのフルパス(拡張なし)]
[03:00:05] LaTeX build process spawned. PID: undefined.
[03:00:05] LaTeX fatal error: spawn latexmk ENOENT, . PID: undefined.
[03:00:05] Does the executable exist? PATH: [ローカル環境の PATH]

 ざっくり解釈すると,ビルドプロセスを立ち上げたけど latexmk が読み込めなくてエラーが起きてるっぽい.

  • エラーについて思ったこと
    • latexmkVSCode に認識されていない
    • シェルを立ち上げたときは latexmk をコマンドとして実行できているため PATH は通してるはず.ただシェル環境と VSCode の環境で PATH の認識の仕方が違う?
    • ↑ と思ったけど,恐らく,これは VSCode の新しいウィンドウの起動時にローカル環境の PATH が読み込まれて,その子プロセスである統合ターミナル中で PATH が更新されてもそれが VSCode 側では更新されないからだと思う.今回の場合,統合ターミナルで MacTeX をインストールして子プロセス内では PATH が書き換えられるけど(Homebrew によって latexmk などが含まれる /Library/TeX/texbin が PATH に追加される),それは親プロセス(ウィンドウ)には反映されない.だから,ウィンドウを再起動して更新された PATHVSCode に読み込ませれば,今度は latexmk に PATH が通されてるから tex ファイルをビルドできるようになる(殴り書きでだらだら書いてごめんなさい).

 これについては VSCode を再起動すれば,ちゃんと latexmk が認識されビルドできるようになる.

5. ビルドはできた(成功したとは言っていない)

5-1. エラーが出た

 今回はクリスマスツリーを出力するコード(How can we draw a Christmas tree with decorations, using TikZ? より引用)をビルドしたが,以下の画像を見ると分かる通り,生成された PDF がダメダメだった.右下にもエラーが出ている.

f:id:pytwbf201830:20191224222910p:plain
ビルド時のエラー

  • エラーメッセージ
Recipe terminated with error. Retry building the project.
  • ログ
[17:51:37] File watcher: responding to change in [tex ファイルのフルパス]
[17:51:37] Parsing [tex ファイルのフルパス]
[17:51:37] [tex ファイルのフルパス] changed. Auto build project.
[17:51:37] BUILD command invoked.
[17:51:37] Building root file: [tex ファイルのフルパス]
[17:51:37] Build root file [tex ファイルのフルパス]
[17:51:37] Recipe step 1: latexmk, -silent,-outdir=out,[tex ファイルのフルパス(拡張子なし)]
[17:51:37] LaTeX build process spawned. PID: 3257.
[17:51:38] Recipe returns with error: 12/null. PID: 3257. message: Latexmk: Run number 1 of rule 'pdflatex'
Latexmk: Summary of warnings from last run of (pdf)latex:
  =====Latex reported missing or unavailable character(s).
=====See log file for details.
Latexmk: Use the -f option to force complete processing,
 unless error was exceeding maximum runs, or warnings treated as errors.
.
[17:51:38] Cleaning auxillary files and retrying build after toolchain error.
[17:51:38] Recipe step 1: latexmk, -silent,-outdir=out,[tex ファイルのフルパス(拡張子なし)]
[17:51:38] LaTeX build process spawned. PID: 3260.
[17:51:38] Recipe of length 1 finished. PID: 3260.
[17:51:38] Successfully built [tex ファイルのフルパス].
[17:51:38] Refresh PDF viewer for [tex ファイルのフルパス(拡張子pdf)]
[17:51:38] Parse fls file.
[17:51:38] Preview PDF file: [tex ファイルのフルパス(拡張子pdf)]
  • 実行されたコマンド(VSCodetex ファイルを編集,保存すると走るコマンド)
$ latexmk -silent -outdir=out christmas_latex.tex
  • 上記ログのエラー箇所
[17:51:38] Recipe returns with error: 12/null. PID: 3257. message: Latexmk: Run number 1 of rule 'pdflatex'
Latexmk: Summary of warnings from last run of (pdf)latex:
  =====Latex reported missing or unavailable character(s).
=====See log file for details.
Latexmk: Use the -f option to force complete processing,
 unless error was exceeding maximum runs, or warnings treated as errors.

 上記のエラーについてググる前に,下記のコマンドでシェルから直接 tex ファイルをビルドしてみた.するとクリスマスツリーの pdf がちゃんと出力された(画像省略).

$ latexmk -pdflatex=lualatex -pdf christmas_latex.tex

 ここから推察するに,LaTeXVSCode でのビルドコマンド?(てか処理エンジン?,補足)が適切に設定されていない(保存されたときに裏側で走るコマンド).恐らく LaTeX Workshop の下記の設定項目(VSCodesettings.json)を設定し直す必要がある.

  • latex-workshop.latex.tools
  • latex-workshop.latex.recipes

 上記二つの設定が適切にできれば VSCode のコマンドパレットの LaTeX Workshop: Build with recipe の選択肢に lualatex とかのビルドコマンドを増やせるはず.

5-2. LaTeX Workshop の設定を見直す

 というわけで LaTeX Workshop の設定を見直す.

  • 現段階での tools,recipes の設定
    • 下記の設定では latexmk のデフォルトの処理系しか使えない.
...
    "latex-workshop.latex.tools": [
        {
            "name": "latexmk",
            "command": "latexmk",
            "args": ["-silent", "-outdir=%OUTDIR%", "%DOC%"]
        }
    ],
    "latex-workshop.latex.recipes": [
        {
            "name": "latexmk",
            "tools": ["latexmk"]
        }
    ],
...

 LaTeX Workshop: Build with recipe のビルドの選択肢を増やすには,tools にコマンド(シェルで実行されるコマンド)を定義し,recipestools に定義したコマンドと VSCode で使うビルドコマンドを対応付けることができる.

 この設定を終えると,VSCode のコマンドパレットから実行する LaTeX Workshop: Build with recipe の選択肢が増えるはず.下記のように toolsrecipes の設定を変更した.

  • 変更後の tools,recipes の設定
    • LaTeX 処理系によってビルドの手順を分けられるようにした.
...
    "latex-workshop.latex.tools": [
        {
            // latexmk を利用した xelatex によるビルドコマンド
            "name": "Latexmk (XeLaTeX)",
            "command": "latexmk",
            "args": [
                "-f",
                "-gg",
                "-pv",
                "-xelatex",
                "-synctex=1",
                "-interaction=nonstopmode",
                "-file-line-error",
                "-outdir=%OUTDIR%",
                "%DOC%"
            ]
        },
        // latexmk を利用した uplatex によるビルドコマンド
        {
            "name": "Latexmk (upLaTeX)",
            "command": "latexmk",
            "args": [
                "-f",
                "-gg",
                "-pv",
                "-synctex=1",
                "-interaction=nonstopmode",
                "-file-line-error",
                "-outdir=%OUTDIR%",
                "%DOC%"
            ]
        },
        // latexmk を利用した platex によるビルドコマンド
        // 古い LaTeX のテンプレートを使いまわしている (ドキュメントクラスが jreport や jsreport ) 場合のため
        {
            "name": "Latexmk (pLaTeX)",
            "command": "latexmk",
            "args": [
                "-f",
                "-gg",
                "-pv",
                "-latex='platex'",
                "-latexoption='-kanji=utf8 -no-guess-input-env'",
                "-synctex=1",
                "-interaction=nonstopmode",
                "-file-line-error",
                "-outdir=%OUTDIR%",
                "%DOC%"
            ]
        },
        // latexmk を利用した lualatex によるビルドコマンド
        {
            "name": "Latexmk (LuaLaTeX)",
            "command": "latexmk",
            "args": [
                "-f",
                "-gg",
                "-pv",
                "-lualatex",
                "-synctex=1",
                "-interaction=nonstopmode",
                "-file-line-error",
                "-outdir=%OUTDIR%",
                "%DOC%"
            ]
        }
    ],
    // latex-workshop.latex.recipes: Recipe の定義
    "latex-workshop.latex.recipes": [
        // XeLaTeX で書かれた文書のビルドレシピ
        {
            "name": "XeLaTeX",
            "tools": [
                "Latexmk (XeLaTeX)"
            ]
        },
        // LaTeX(upLaTeX) で書かれた文書のビルドレシピ
        {
            "name": "upLaTeX",
            "tools": [
                "Latexmk (upLaTeX)"
            ]
        },
        // LaTeX(pLaTeX) で書かれた文書のビルドレシピ
        {
            "name": "pLaTeX",
            "tools": [
                "Latexmk (pLaTeX)"
            ]
        },
        // LuaLaTeX で書かれた文書のビルドレシピ
        {
            "name": "LuaLaTeX",
            "tools": [
                "Latexmk (LuaLaTeX)"
            ]
        }
    ]
...

 設定し終えたため,再度ビルドを実行する.コマンドパレットから LaTeX Workshop: Build with recipe を検索するとちゃんと選択肢が増えてる(先ほどまでは latexmk しかなかった!).

f:id:pytwbf201830:20191224223018p:plain

 クリスマスツリーを表示する今回の tex ファイルは処理系に LuaLaTeX が必要だった.上記の選択肢の内,LuaLaTeX を選択しビルドを実行するとついにクリスマスツリーが表示された!(12/24 22 時過ぎ時点).

Xmas tree with LuaLaTex
Xmas tree with LuaLaTeX

補足:LaTeX エンジンの種類と pdf 出力のワークフロー

 LaTeX エンジンは LaTeX の処理系のことである.

  • LaTeX エンジンの種類
    • dvi ファイルを出力するタイプのエンジン
    • pdf ファイルを直接出力するタイプのエンジン
      • pdfLaTeX
      • XeLaTeX
      • LuaLaTeX

 最近では,エンジンの種類に関わらず PDF ファイルを成果物とすることが普通だが,「dvi から pdf への変換」について,現状では「dvips を使う」,「dvipdfmx を使う」という 2 種類の方式が並存している.「LaTeX を使って pdf ファイルを得る」というワークフローについては以下 7 つの方式があることになる.

  • pLaTeX + dvips
  • pLaTeX + dvipdfmx
  • upLaTeX + dvips
  • upLaTeX + dvipdfmx
  • pdfLaTeX
  • XeLaTeX
  • LuaLaTeX

教訓

  • 自分はネットの記事を頼ることしかできないので,LaTeX の環境構築系の記事は本当にありがたい.しかし,ネットの記事通りにやっても大体上手く行かない(これは LaTeX 環境の構築に限らないけども).
  • エラーメッセージ,ログをちゃんと読めば解決できる.

終わりに

 LaTeX の環境構築はハマると辛いイメージがあるので私の書いた記録が誰かの助けになれば幸いです.ハマりどころはそう多くはないはずなので虱潰しに頑張りましょう.

Merry Xmas.

$ xmas
                ____________________________________
               I___|___|___|___|___|__|____|___|__|_I
               I_|___|___|               |___|___|__I
         )\    I___|__ | ..,a@@@a,a@@@a,.. |___|____I      /(
        ( ))   I_|__  .,;*;;@@@@@a@@@@@;;;;,. ___|__I     (( )
         :     I__|  ;;;;;;;;;a@@^@@a;;;*;;;;;  __|_I       :
        ,uU    I_|  ;;;;*;;;a@@@   @@@a;;;;*;;;  |__I      Uu.
        :Uu    I__|;;;;;;;a@@@@   .@@@@@;;;;;;;; __|I      uU:
        | |    I_| ;;*;;;a@@@@@   @@'`@@@;;;;;*; _|_I      | |
        |_|    I__ ;;;;;;@@;;@@   `@  `@;;;*;;;; ___I      |_|
     _ (___) _ I_|_ ;;;*;;@;;;;@;;;;;*;;;;;;;;; _|__I___  (___)
   ,-' )   (   ~~~~~ `;;;;;;*;;;;;;;;;;;*;;;;'  ~~~~~     )   (`-.
 ,-____=====_____________`;;;;;;;;*;;;;;;;'_______________=====___`.
 |~~|  _________________________________________________/o\___   |~~|
 |_||  ||____|____|____|____|____|____|____|____|____|_/ /,\__|  ||_|
 |__|  |___|____|____|____|____|____|____|____|____|__/ /,,,\||  ||_|
 |_||__||____|____|____|____|____|____|____|____|____|\/,,,,,\|__|__|
 |____|____|____|____|____|____|____|____|____|____|___\,,,,,,\___|_|
 |_|____|__I####I..........  /%%%%%%%%%%%\ ..........I##\,,,,( )|___|
 |____|____I####I.......... .%%%%%( )%%%%%. .........I###\,,,,\/__|_|
 |_|____|__I####I.......... @@%%%%0%0%%%%@@  ........I## /,,,,/_|___|
 |____|____I####I.......... `@@@@@@@@@@@@@@' ........I# /,,,,/____|_|
 |_|____|__I####I............ \\\\\\\\\\\\\) ........I ( \,,/___|___|
 |____|____I####I.............  `\\\\\\\\\\) ........I  \_)/_|____|_|
 |_|____|__I####I............  A   `\\\\\\\' ..   .. I#  :_|____|___|
 |____|____I####I.........    AAA  .. `\\\' ..  A.  .I###I___|____|_|
 |_|____|__I####I......   .A  `AAA ....  *  ..  AAA. I ##I_|____|___|
 |____|____I####I....    AAA  AA;AA  ...   ...  `AAA.I ##I___|____|_|
 |_|____|__I####/~~~,-.A;;A'-A;;;;;A-----A-----,A;;;A  ##I_|____|___|
 |____|____I###/    I.;;;;;  ;;;;;;;   AAA     I;;;A'\###I___|____|_|
 |_|____|__I##/     I;;;;;; ;;;;;;;   A;;;A    I;;;;  \##I_|____|___|
 |____|____I#/     !~;;;;;;~~~~~~~~~~~;;;;;;~~~~~!;'   \#I___|____|_|
 |_|____|__I/______!  ::::;;           ;;;;;;    !______\I_|____|___|
 ~~~~~~~~~/       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~       \~~~~~~~~~~
         /_______________________________________________\
                        /\____/\    __
                      .'  """"  `,-'  `--.__
                 __,- :   -  -  ;  " ::     `-. -.__
              ,-sssss `._  `' _,'"     ,'~~~::`.sssss-.
             |ssssss ,' ,_`--'_    __,' ::  `  `.ssssss|
            |sssssss `-._____~ `,,'_______,---_;; ssssss|
             |ssssssssss     `--'~{__   ____   ,'ssssss|
              `-ssssssssssssssssss ~~~~~~~~~~~~ ssss.-'
                   `---.sssssssssssssssssssss.---'

------------------------------------------------

sited by asciiart.website.

(クリスマスネタが 2 つでくどいけど可愛いアスキーアートだったので alias にしてみた)