3.5分makeしてみようパターンルールより実用的にtargetsとの使い分けまとめ本稿では「Rユーザーのためのmake入門」と題して、makeというビルドツールを紹介します。makeはRに限らずさまざまなプロジェクトで汎用的に使われており、使い慣れておくと日常のコマンド実行を効率化できるかもしれません。今回はMakefileの基本的な書き方から、複数のRmdファイルをmakeを使って一括で変換する例を紹介します。締切直前に複数のRmdファイルを編集していたとしても、確実にすべてのレポートをコマンド1発で最新にできるようになるでしょう。makeはすでにお使いの環境にインストール済みかもしれませんし、インストールされていなかったとしても、パッケージマネージャなどで簡単にインストールできるでしょう。本稿では再現性のため、Docker上でRStudioやmakeを動かします。動作確認はrocker/tidyverse:4.2.1で行っています。Password:foobarでログインしてください。rocker/tidyverse:4.2.1makeしてみようRStudioを使っていれば、通常はKnitボタンを押してRmdファイルを変換するでしょう。Consoleからrmarkdown::renderを呼び出すことでも行えます。rmarkdown::render("example.Rmd")また、以下のコマンドでターミナルから別のRのプロセスを起動してRmdファイルを変換することもできます。'rmarkdown::render("example.Rmd")'Makefileには以下のように変換のルールを指定します。commandstarget:作成したいターゲットdependencies:ターゲットが依存するもの、つまりdependenciesを元にターゲットが作られるcommands:作成するためのコマンド今回の例ではRmdファイルをもとにhtmlファイルを生成したいので、以下のように記述します。Makefileと名前を付けて保存してください。'rmarkdown::render("example.Rmd")'コマンドの先頭、つまりRscriptから始まる行の先頭にはタブ文字を入力します。スペースではなくタブです。RStudioではMakefileという名前のファイルでTabを押すとタブ文字が入力されるはずです。characters」にチェックを入れてください。図1のようにタブ文字がグレーの横棒で可視化されるようになります。空白文字を可視化makeを実行してみましょう。<ターゲット>を実行すると、makeはMakeFileを読み込み、指定されたターゲットを生成します。'rmarkdown::render("example.Rmd")'Rmdファイルを変換するコマンドが実行され、ターゲットであるhtmlファイルが出力されました。ちなみにターゲットが指定されていない場合、Makefileで最初に定義されたターゲットを生成します。この場合は単にmakeを実行しても大丈夫です。make2回続けてmakeを実行するとどうなるでしょう。date.makeは「example.htmlは最新です」と表示し、何もしません。makeはターゲットが古くなっているとき、つまりターゲットよりも元となるファイルが新しいときのみ変換コマンドを実行するためです。元となるファイル、example.Rmdを編集した後makeを実行すると、makeは再びコマンドを実行してRmdファイルをhtmlファイルに変換します。このように、makeは何を編集したら、何を実行すべきかを判断して実行してくれるツールです。パターンルールターゲットが1つだけの単純な例では、makeの恩恵を感じられないかもしれません。複雑な例を紹介します。複数のRmdファイルを変換したいケースを考えましょう。以下のように変換したいRmdファイルの数だけルールを列挙することはできます。'rmarkdown::render("example2.Rmd")'しかし、これはほぼ同じ内容の繰り返しで冗長で分かりづらく、書き間違える可能性もあります。コマンド部分を修正したい場合、すべての該当箇所を書き換える必要があります。makeにはパターンルールというしくみがあります。パターンルールはターゲットの指定に%を含むことができ、%は空ではない任意の文字列にマッチします。%.Rmdのように記述でき、素直に「Rmdファイルからhtmlファイルを生成する」と読めるでしょう。パターンルールを使うと、任意のRmdファイルの変換を以下のように指定できます。'rmarkdown::render("$<")'$<という見慣れない記号が現れました。これは自動変数と呼ばれるもののひとつで、ソースファイルの名前に置換されます[1]。'rmarkdown::render("example1.Rmd")'が実行されます。makeには数多くの自動変数が用意されています。詳細はマニュアルを参照ください。https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.htmlパターンルールによって、任意の名前のRmdファイルが変換できるようになりました。example2.htmlより実用的にこれまでの例では、ターゲットは作成したいファイル名を指していました。makeでは擬似ターゲットという、実存しない抽象的なターゲットを定義できます。makeには.PHONYという特殊なターゲットが用意されていて、.PHONYの依存項目に指定したものを疑似ターゲットに定義できます。example2.htmlここでhtmlは疑似ターゲットで、htmlをターゲットに指定すると、依存項目であるexample1.html、example2.htmlを生成しようとします。Makefile全体は以下のようになります。html、または単にmakeを実行するだけで、変更のあったRmdファイルだけを変換できるようになりました。使われる疑似ターゲットにcleanがあります。cleanは通常はmakeで生成したファイルをすべて削除して、makeを実行前の状態に戻すために使われます。今回の例では以下のように指定するとよいでしょう。cleanでhtmlファイルすべてを削除できるようになります。*.htmlは必ず実行されます。ここまで使いこなせると、Makefileがずいぶん実用的になります。html、または単にmakeを実行すれば常に成果物となるhtmlファイルは最新になります。複数のRmdファイルを更新してうっかりそのうちの1つを変換し忘れる、ということもなくなるでしょう。Makefileを書いたおかげで、何を生成するためにどんなコマンドを実行するべきかを覚えておく必要がなくなりました。あともう少し工夫して、Makefileをより汎用的にしましょう。他のプログラミング言語同様、makeは変数を扱うことができます。値に適切な名前を付けることで、それが何であるかを把握しやすくなります。先ほどのhtmlターゲットを変数を使って書き換えてみましょう。$(htmls)変数名を$()で囲うことで値を取り出すことができます。ところで、htmlファイルをひとつひとつ列挙するのは大変ですし、対象となるファイルが増えるたびにMakefileを修正するのも面倒です。ワイルドカードを使って、*.htmlのようにすべてのhtmlファイルを指定したいですよね。変数の値にワイルドカードで指定したファイル名を展開するには、wildcard関数を使います。*.html)実はこの指定方法では十分ではありません。cleanを実行した直後はワイルドカードにマッチするhtmlファイルが存在せず、htmlsターゲットが変換する対象が空になってしまい、何も実行されないからです。ソースとなるRmdファイルを列挙し、その結果を元にターゲットのhtmlファイルのリストを作りましょう。以下のように指定します。$(rmds:.Rmd=.html)$(rmds:.Rmd=.html)は置換参照という機能を使って、rmdsの値の中の.Rmdをすべて.htmlに置き換えています。これで任意のRmdファイルをhtmlに変換できるようになりました。Makefile全体は以下のようになります。*.htmltargetsとの使い分け「#R登山本」の呼称で好評いただいている『Rが生産性を高める』の第7章で紹介したtargetsパッケージもまた、処理同士の依存関係を管理して実行できます。実現したい内容によって使い分けると良いでしょう。各タスクがファイルを入出力(ファイルを読み込んで別のファイルを書き出す)する場合は、汎用的なmakeを使うことができます。逆にRを実行中にRのオブジェクトの変更を検知したいのであれば、targetsパッケージを使うべきでしょう。まとめ本稿では、Makefileの基本的な書き方、そして実用的なMakefileを書くためのいくつかのしくみを説明しました。makeは一度書けば、欲しいもののために何をすべきかを判断して実行してくれる、動く手順書と言えるでしょう。makeはとても多機能なツールで、この記事で紹介し切れなかった機能がたくさんあります。使いこなしたい場合は公式のマニュアルを参照してください。https://www.gnu.org/software/make/manual/では良いコマンドラインライフを。Ltd.ページ内容の全部あるいは一部を無断で利用することを禁止します⁠。個別にライセンスが設定されている記事等はそのライセンスに従います。