Makefile から Taskfile へ移行するモチベーションの言語化
はじめに
タスクランナーを Makefile から Taskfile へと移行したいと考え、社内の他の開発者へ説明する機会がありました。
そのときの資料の転記になります。
すでに以下の記事があり、内容を加筆したものになります。
gsy0911
さんの記事がなければここまでまとめようと思いませんでした。ありがとうございます。
本記事ではタスクランナーとしての Makefile と Taskfile を比較し、そのメリット・デメリットを洗い出すことを目的としています。
Taskfile の機能の説明を網羅したものでない点にご注意ください。
先にまとめ
モチベーションをまとめるとこの2つ。
- Makefile の潜在的なリスクを回避したい
- Taskfile による豊富な機能を享受したい
Makefile にはタブインデントや OS の環境差異のリスクがあると考えています。
Makefile はビルドツールのため、タスクランナーとして使用しようとすると追加の複雑さが必要な印象を受けました。
Taskfile にはタスクランナーとしての豊富な機能を活用できるメリットがあると考えています。
また、CI で make
コマンドを使用していると影響範囲がそれなりに広がります。
規模が大きいアプリケーションでは修正箇所の把握、段階的な移行が大切であると考えます。
メリット・デメリットの概要
この後のセクションには詳細を書いていますが、その全体像を以下に示します。
- Makefile の課題感
- タブインデント
-
.PHONY
の指定 - OS の環境差異
- Taskfile のメリット
- クロスプラットフォーム対応
- Makefile にない豊富な機能
- yaml 形式による視認性
- 特に「Makefile にない機能」との相乗効果で、豊富な機能を享受しつつ視認性を確保できる
- 移行コスト
-
Makefile
からTaskfile.yaml
へ書き換え - ローカルに
task
をインストール - CI で
task
をセットアップ -
README.md
、社内ドキュメントのmake
をtask
へ書き換え
-
Makefile の課題感
タブインデント
- Makefile はタブインデントでないと動作しない
- タブインデントが原因で実行エラーとなったとき、デバッグしづらい (気づきづらい)
.PHONY
を指定する必要がある
ファイルを生成しないコマンドには タスクランナーとしてみたとき、 .PHONY
の指定は不要な記述となります。
Makefile
.PHONY: hello
hello:
echo "Hello, World!"
Taskfile
version: '3'
tasks:
hello:
cmds:
- echo "Hello, World!"
Windows 環境では make コマンドが動作しないパターンがある
Windows10環境でmakeコマンドを使用する方法【ハマリ回避】
- Program Filesフォルダにインストールすると、makeコマンドのパスにスペースが入ってうまく動かない
- MinGWをインストールする際にmsysを一緒にインストールするとmakeプログラムもインストールされるが、この方法でインストールされたmakeは一部の組み込み変数の値がおかしくなっている
Taskfile のメリット
クロスプラットフォーム対応
- どのプラットフォームでも追加の設定などすることなく、動く
yaml 形式による視認性
特にファイルの内容が多くなってきたとき、構造化されていると認知不可が低い。
Makefile
.PHONY: build test clean
build:
echo "Building the project..."
test:
echo "Running tests..."
clean:
echo "Cleaning up..."
Taskfile
version: '3'
tasks:
build:
desc: "Build the project"
cmds:
- echo "Building the project..."
test:
desc: "Run tests"
cmds:
- echo "Running tests..."
clean:
desc: "Clean up"
cmds:
- echo "Cleaning up..."
Taskfile の機能と比較
説明
Makefile
.PHONY: hello
hello: ## say hello
echo "Hello, World!"
Taskfile
version: '3'
tasks:
hello:
# `desc` でタスクの説明を追加
desc: say hello
cmds:
- echo "Hello, World!"
タスクリスト
Makefile
- サポートなし
Taskfile
version: '3'
tasks:
hello:
desc: This task says Hello, World!
cmds:
- echo "Hello, World!"
# -l オプションでタスクリストを表示
$ task -l
task: Available tasks for this project:
* hello: This task says Hello, World!
default と組み合わせて設定することもできる
version: '3'
tasks:
default:
cmds:
- task -l --sort none
hello:
desc: This task says Hello, World!
cmds:
- echo "Hello, World!"
$ task
task: Available tasks for this project:
* hello: This task says Hello, World!
引数
Makefile
.PHONY: hello
hello:
echo "Hello $(USER_NAME)"
$ USER_NAME=taro make hello
hello taro
Taskfile
version: '3'
tasks:
hello:
cmds:
- echo "hello {{.CLI_ARGS}}"
$ task hello -- taro
hello taro
ワイルドカード
Makefile
Taskfile
version: '3'
tasks:
echo-*-*:
vars:
ARG_1: '{{index .MATCH 0}}'
ARG_2: '{{index .MATCH 1}}'
cmds:
- echo {{.ARG_1}} {{.ARG_2}}
$ task echo-foo-bar
foo bar
内部タスク
Makefile
- サポートなし
Taskfile
version: '3'
tasks:
build-image-1:
desc: build image-1
cmds:
- task: build-image
vars:
DOCKER_IMAGE: image-1
build-image:
# 内部タスクとして指定。task XXX で実行不可。再利用可能な関数のように利用できる。
internal: true
desc: build image
cmds:
- docker build -t {{.DOCKER_IMAGE}} .
$ task -l
task: Available tasks for this project:
* build-image-1: build image-1
プラットフォーム固有のタスク
Makefile
- サポートなし
Taskfile
version: '3'
tasks:
build-windows:
# windows でしか実行されない。他の OS ではスキップされる。
platforms: [windows]
cmds:
- echo 'Running command on Windows'
依存タスクの並列実行
Makefile
- サポートなし
Taskfile
version: '3'
tasks:
build:
deps: [assets]
cmds:
- go build -v -i main.go
assets:
cmds:
- esbuild --bundle --minify css/index.css > public/bundle.css
依存タスクの直列実行
Makefile
.PHONY: hello
task-to-be-called:
echo "Task to be called"
another-task:
echo "Another task"
hello: task-to-be-called another-task
echo "Hello, World!"
Taskfile
version: '3'
tasks:
main-task:
cmds:
- task: task-to-be-called
- task: another-task
- echo "Both done"
task-to-be-called:
cmds:
- echo "Task to be called"
another-task:
cmds:
- echo "Another task"
実行ディレクトリの指定
Makefile
.PHONY: cd-ls
cd-ls:
(cd example-dir && ls)
Taskfile
version: '3'
tasks:
cd-ls:
# 実行ディレクトリを指定
dir: example-dir
cmds:
- ls
タスクごとの変数定義
Makefile
- サポートなし
Taskfile
version: '3'
tasks:
hello:
desc: This task says Hello, World!
cmds:
- echo "{{.LOCAL_MESSAGE}}, World!"
# タスクごとに定義
vars: { LOCAL_MESSAGE: 'Hello' }
グローバルな変数定義
Makefile
MESSAGE := Hello
.PHONY: hello
hello:
echo "$(MESSAGE), World!"
Taskfile
version: '3'
vars: { MESSAGE: 'Hello' }
tasks:
hello:
desc: This task says Hello, World!
cmds:
- echo "{{.MESSAGE}}, World!"
動的な値の変数定義
Makefile
$(eval AWS_ACCOUNT := $(shell aws sts get-caller-identity | jq -r .Account))
.PHONY: hello
hello:
echo "$(AWS_ACCOUNT)"
Taskfile
version: '3'
vars:
AWS_ACCOUNT:
sh: aws sts get-caller-identity | jq -r .Account
tasks:
hello:
cmds:
- echo "{{.AWS_ACCOUNT}}"
dotenv との統合
Makefile
- サポートなし
Taskfile
USER_NAME=taro
version: '3'
tasks:
hello:
cmds:
- echo "hello $USER_NAME!"
dotenv: ['local.env']
設定ファイルの分割
Makefile
docker-build:
echo "build"
include docker.mk
.PHONY: hello
hello:
echo "Hello, World!"
$ make docker-build
build
Taskfile
version: '3'
tasks:
build:
desc: build image
cmds:
- echo "build"
version: '3'
includes:
docker: ./Taskfile.docker.yml
tasks:
hello:
desc: This task says Hello, World!
cmds:
- echo "Hello, World!"
$ task -l
task: Available tasks for this project:
* hello: This task says Hello, World!
* docker:build: build image
$ task docker:build
build
移行コスト
移行コストについてもまとめようと思います。
タスクランナーを CI で使用している場合、思ったよりも影響範囲は大きいです。
- ローカル
-
task
のインストール -
Makefile
をTaskfile.yaml
へ書き換え
-
- CI
-
task
のインストール -
make
コマンドをtask
コマンドへ書き換え
-
- ドキュメント
-
README.md
などの書き換え
-
CLI インストール
mac
$ brew install go-task/tap/go-task
$ brew install go-task
npm
$ npm install -g @go-task/cli
go
$ go install github.com/go-task/task/v3/cmd/task@latest
GitHub Actions セットアップ
- name: Install Task
uses: arduino/setup-task@v1
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
おわりに
いかがだったでしょうか。ぜひみなさんも Taskfile を触ってみてください。
Discussion
移行コストについて追記しました。