[[📒Articles]] > [[📒2021 Articles]] ![[2021-08-08.jpg|cover-picture]] [[Task]]という[[タスクランナー]]を試してみたところ、[[Make]]と比べて、特に[[Windows]]では優れているポイントがいくつもあったので記事を書いてみました。 ## はじめに [[タスクランナー]]とはタスク単位で処理を自動化するツールのことです。 [[Node.js]]なら[[npm]]、[[Rust]]なら[[Cargo]]のようにエコシステムが[[タスクランナー]]としての機能をもつケースもあります。しかし、それが提供されていない言語も多く、[[タスクランナー]]によって仕様が異なるため[[コンテキストスイッチ]]の切り替えコストも高いです。 そんな課題がある中、2015年頃に様々な環境で使える[[タスクランナー]]が話題になったことがありました。[[Make]]です。 <div class="link-card"> <div class="link-card-header"> <img src="https://www.gnu.org/graphics/gnu-head-mini.png" class="link-card-site-icon"/> <span class="link-card-site-name">www.gnu.org</span> </div> <div class="link-card-body"> <div class="link-card-content"> <div> <p class="link-card-title">Make - GNU Project - Free Software Foundation</p> </div> </div> </div> <a href="https://www.gnu.org/software/make/"></a> </div> [[Make]]は[[Linux]]ベースの環境ではほぼプリインストールされているため、利用することへのハードルが低いです。`Makefile`さえ書いてしまえば実行するだけです。ところが、一部の環境では別の苦しみに悩まされていたのも事実です。[[Windows]]です。 ### [[Windows]]で[[Make]]を使うことの苦しみ [[Windows]]で[[Make]]を使うときハマりやすいポイントが2つあります。 #### 文字コード 文字コードについては言うまでもないでしょう。[[Windows]]のターミナルはエンコーディングとして[[Shift_JIS]]を採用していることが多いです。一方、その他は[[UTF-8]]です。 この性質から **`Makefile`で日本語の入出力を取り扱いとき、==文字化け==に悩まされる** ことが非常に多いです。[[Windows]]だけでしか利用しないならまだマシですが、他のOSで開発することがある場合は地獄でしょう。 #### シェル 2つ目はシェルの問題です。[[Windows]]以外の環境であればデファクトスタンダードなシェルは[[Bash]]だと思います。[[Bash]]で動くスクリプトを書いておけばほとんどの場合は問題ないでしょう。 一方、[[Windows]]の標準スクリプトは[[cmd]]です。最近は[[PowerShell]]に移行しつつあるものの、その文法は[[Bash]]と異なります。通常は[[Bash]]用に作成したスクリプトが期待通り動くことなどないでしょう。 そして、[[Windows]]ユーザーの環境に[[Bash]]を適合しようとすると、これまた混乱を招くことが多いです。詳細は割愛しますが、各々の環境下における[[Bash]]の挙動が微妙に異なるため、諦めて[[PowerShell]]で処理を書き直したことが多々あります。 ## [[Task]]とは さて、ここからが本題です。まずは[[Task]]を紹介します。 <div class="link-card"> <div class="link-card-header"> <img src="https://github.githubassets.com/favicons/favicon.svg" class="link-card-site-icon"/> <span class="link-card-site-name">GitHub</span> </div> <div class="link-card-body"> <div class="link-card-content"> <div> <p class="link-card-title">GitHub - go-task/task: A task runner / simpler Make alternative written in Go</p> </div> <div class="link-card-description"> A task runner / simpler Make alternative written in Go - GitHub - go-task/task: A task runner / simpler Make alternative written in Go </div> </div> <img src="https://opengraph.githubassets.com/f7434eb47dfa8785af13efb144742021ba788201a349b3ecaa9f037e2b4f431e/go-task/task" class="link-card-image"/> </div> <a href="https://github.com/go-task/task"></a> </div> 主な特徴は以下の通りです。 - タスク定義は[[YAML]]で書く - シングルバイナリで各OSの[[パッケージマネージャー]]によるインストールに対応 - クロスプラットフォーム対応 クロスプラットフォーム対応なので[[Windows]]サポートもバッチシです。 > Truly cross-platform: while most build tools only work well on Linux or macOS, Task also supports Windows thanks to this awesome shell interpreter for Go; [[インタプリタ]]として[[sh]]を使っているため、[[Bash]]がインストールされていない[[Windows]]でも[[Bash]]の処理を記述できているようです。 ## [[Task]]をいじってみる 普段は[[PowerShell]]を使っていますが、ここではコマンドプロンプトを使うことにします。 ### タスク定義ファイルの作成 `task --init`コマンドで一瞬です。 ```cmd: > task --init Taskfile.yml created in the current directory ``` 中身は『Hello, World!』を出力するコマンドになっています。 ```yaml:Taskfile.yml version: '3' vars: GREETING: Hello, World! tasks: default: cmds: - echo "{{.GREETING}}" silent: true ``` ### タスクの実行 `task`コマンドで`tasks.default`のタスクが実行されます。 ```cmd: > task Hello, World! ``` 変数`GREETING`はコマンドから指定できます。 ```cmd: > task GREETING=はろー世界!! はろー世界!! ``` 設定によっては[[タスクのコマンド定義を簡略化]]できます。ついでに`default`以外のタスクを設定してみました。 ```yaml:Taskfile.yml version: "3" silent: true tasks: run: echo run run2: - echo run2-1 - echo run2-2 run3: cmds: - echo run3-1 - echo run3-2 ``` `default`以外のタスクを実行する場合は、`task`コマンドのあとに指定します。 ```cmd: >task run run >task run2 run2-1 run2-2 >task run3 run3-1 run3-2 ``` ### 日本語の取り扱い [[UTF-8]]の`Taskfile.yml`に日本語でデフォルト値を設定してみます。 ```yaml:Taskfile.yml version: '3' vars: GREETING: はろー世界!! tasks: default: cmds: - echo "{{.GREETING}}" silent: true ``` コマンドプロンプトのエンコーディングは[[Shift_JIS]]ですが、文字化けせずにしっかり表示されます。 ```cmd: > task はろー世界!! ``` ちなみに[[UTF-8]]の`Makefile`に対し、[[Make]]で実行するとこうなります。 ```cmd: > make run 縺ッ繧阪・荳也阜!! ``` ## よく使う書き方 [[YAML]]の記載ルールはすべてUsageで紹介されています。 <div class="link-card"> <div class="link-card-header"> <img src="https://taskfile.dev/favicon.ico" class="link-card-site-icon"/> <span class="link-card-site-name">taskfile.dev</span> </div> <div class="link-card-body"> <div class="link-card-content"> <div> <p class="link-card-title">Usage</p> </div> <div class="link-card-description"> Create a file called Taskfile.yml in the root of your project. The cmds attribute should contain the commands of a task. </div> </div> </div> <a href="https://taskfile.dev/#/usage"></a> </div> この中で私がよく使う書き方をいくつか紹介します。 ### [[Taskの環境変数設定]] `env`で指定できます。 ```yaml:Taskfile.yml version: "3" silent: true tasks: default: cmds: - echo Hello $NAME env: NAME: tadashi-aikawa ``` ```cmd: > task Hello tadashi-aikawa ``` `dotenv`プロパティで`.env`ファイルを読み込む方法もあります。 ### [[変数の設定]] 変数は`${VAR}`ではなく`{{.VAR}}`の形式で表現されます。 ```yaml:Taskfile.yml version: "3" silent: true tasks: default: cmds: - echo Hello {{.NAME}} ``` 変数は`task`コマンドの後ろで指定します。[[Bash]]のように`NAME=xxx task`と指定できるスクリプト言語もありますが、[[Windows]]の[[cmd]]や[[PowerShell]]でも互換性をとるために必要です。 ```cmd: > task NAME=tadashi-aikawa Hello tadashi-aikawa ``` #### デフォルト値の設定 `vars`でデフォルト値を指定できます。 ```yaml:Taskfile.yml version: "3" silent: true vars: NAME: World tasks: default: cmds: - echo Hello {{.NAME}} ``` 一方、各タスクの中で`vars`を設定すると、`task`コマンドで指定できなくなります。これが仕様かは不明です。 ```yaml:taskコマンドで指定して上書きできないNAME tasks: default: cmds: - echo Hello {{.NAME}} vars: NAME: World ``` #### コマンド結果の値を変数に設定 `sh: `を組み合わせることで動的な変数設定ができます。 ```yaml:Taskfile.yml version: "3" silent: true tasks: default: cmds: - echo Hello {{.NAME}} vars: NAME: sh: head -1 Taskfile.yml | cut -d' ' -f 2 ``` ```cmd: > task Hello 3 ``` ### [[Taskで実行するディレクトリを指定]] デフォルトでは`cmds`で指定したコマンドは、`Taskfile.yml`の配置場所をカレントディレクトリとして実行されます。`dir`でこれを変更できます。 以下の構成で、`.vscode`配下でコマンドを実行したいとします。 ```ls  . ├──  .vscode │ └──  tasks.json ├──  Makefile └──  Taskfile.yml ``` `dir: .vscode`を指定します。 ```yaml:Taskfile.yml version: "3" silent: true tasks: default: dir: .vscode cmds: - ls -l ``` ```cmd: > task total 4 -rw-rw-r-- 1 syoum syoum 341 Aug 08 19:47 tasks.json ``` なお、以下の書き方は期待通り動きません。`cmds`の各コマンドは`Taskfile.yml`の配置場所をカレントディレクトリとして実行されるからです。 ```yaml default: cmds: - cd .vscode - ls -l ``` ### [[タスクの依存関係を設定]] [[Make]]と同様にタスクの依存関係を設定できます。`deps`で指定します。 ```yaml:Taskfile.yml version: "3" silent: true tasks: hello: echo Hello sorry: echo Sorry bye: echo Bye default: deps: - hello - sorry - bye cmds: - echo "Run default" ``` 実行します。 ```cmd: > task Bye Sorry Hello Run default ``` なお、いずれかのタスクを失敗させると以下のように出力され中断します。 ```cmd: > task Bye Hello task: Failed to run task "sorry": exit status 1 ``` `deps`で指定した複数のタスクは、`task`コマンドで指定したタスクを実行する前に[[並列]]に実行されます。そのため、==順番に依存する==複数の依存関係は指定できません。その場合は[[別のタスクを実行]]するといいでしょう。 ### [[別のタスクを実行]] コマンドに`task: `と指定するとタスクから別のタスクを実行できます。 ```yaml:Taskfile.yml version: "3" silent: true tasks: hello: echo Hello sorry: echo Sorry bye: echo Bye default: cmds: - task: hello - task: sorry - task: bye - echo "Run default" ``` [[タスクの依存関係を設定]]する場合と異なり、順番が保証されます。 ```cmd: > task Hello Sorry Bye Run default ``` ### 条件を満たす場合のみ実行可能にする 必須のパラメータが指定されなかったときにエラーを出す..というよくあるケースです。[[Make]]で[[クロスプラットフォーム]]対応しようとするとかなり大変ですが、[[Task]]ではエレガントに書けます。 ```yaml:Taskfile.yml version: "3" silent: true tasks: release: cmds: - echo Release version {{.VERSION}} preconditions: - sh: "[ {{.VERSION}} != '' ]" msg: "VERSION is required." ``` `VERSION`を指定しなかった場合はエラーになります。 ```cmd: > task release task: VERSION is required. task: precondition not met > task release VERSION=1.2.3 Release version 1.2.3 ``` 実際にコマンドが実行された結果はちゃんとカラーリングされるのでエラーも分かりやすいです。 ### タスク一覧と説明の表示 `task <command>`で対応している`<command>`と説明の一覧を表示するには`task -l`コマンドを使います。ただし、コマンドを一覧に表示させるには`desc`に説明文を書く必要があります。 ```yaml:Taskfile.yml version: "3" silent: true tasks: default: - task: build build: cmds: - echo build test: desc: テストを実行する cmds: - echo test run: cmds: - echo run release: desc: "成果物をリリースする (ex: task release VERSION=1.2.3)" cmds: - echo Release version {{.VERSION}} preconditions: - sh: "[ {{.VERSION}} != '' ]" msg: "VERSION is required." ``` `test`と`release`だけに`desc`を追加しました。`task -l`の結果は以下のようになります。 ```cmd: > task -l task: Available tasks for this project: * release: 成果物をリリースする (ex: task release VERSION=1.2.3) * test: テストを実行する ``` ## [[YAML]]ファイルのスタイルガイド [[Task]]には[[YAML]]のスタイルガイド、つまりフォーマットルールのようなものがあります。全部で10にも満たないので一通り見ておくといいでしょう。 <div class="link-card"> <div class="link-card-header"> <img src="https://taskfile.dev/favicon.ico" class="link-card-site-icon"/> <span class="link-card-site-name">taskfile.dev</span> </div> <div class="link-card-body"> <div class="link-card-content"> <div> <p class="link-card-title">Styleguide</p> </div> <div class="link-card-description"> This is the official Task styleguide for Taskfile.yml files. This guide contains some basic instructions to keep your Taskfile clean and familiar to other users. </div> </div> </div> <a href="https://taskfile.dev/#/styleguide"></a> </div> ## まとめ [[Windows]]にも優しい[[タスクランナー]]、[[Task]]を紹介しました。 [[Windows]]以外の面でも[[Make]]と比較して[[タスクランナー]]に便利な機能が詰まっていると思ります。[[クロスプラットフォーム]]対応やポータビリティの面も含めて移行してみる価値はあるのではないでしょうか。