makeコマンドの代用ツールを探していて、doitというツールがあることを知りました。
その使い方をChatGPTに聞いたり、公式ドキュメントを読んで理解しようとしましたが、割とクセの強い仕様で、公式ドキュメントも少し読みづらく感じました。
以下はそれを補うために、ChatGPTに整理してもらい、逆引き的な使用説明書にしたものです。
doit クイックガイド
doit とは
doit は Python 製の強力なタスク自動化ツール(ビルドツール)です。C言語における make のように、ファイルの依存関係や状態変化を自動判定し、更新が必要なタスクのみを実行(増分実行 / Incremental Execution) します。
基本的な特徴
- Python による柔軟な記述: 複雑な制御構文や外部ライブラリとの連携が容易です。
- シェルと Python 関数の融合: シェルコマンドだけでなく、Python 関数をそのままアクションとして指定できます。
- 自動的なキャッシュ管理: ファイルのハッシュ値や実行結果を .doit.db に記録・監視します。
基本のファイル構成(dodo.py)
doit はカレントディレクトリの dodo.py を探して読み込みます。タスクを定義するには、task_ で始まる関数(Task Creator) を作成し、定義辞書(Task Dictionary)を返します。
シンプルなdodo.pyの例
def task_hello():
"""簡単な挨拶を出力するタスク"""
return {
'actions': ['echo "Hello, doit!"'],
}
makeコマンドとの違い
- doitはmakeコマンドの互換ツールではなく、タスク定義はmakeコマンドとは全く異なる書式で定義します
- ファイルの更新などの際に実行するコマンドとして、通常の外部コマンドだけでなく、pythonコードも指定できます
- ファイルの更新の要否をmakeコマンドではファイルの最終更新日時を用いますが、doitでは独自に保管したハッシュ値で確認します
- makeコマンドでは拡張子
.cのファイルから.oのファイルを生成するルールの様な汎用のルールが定義できますが、doitではターゲットはファイル名で指定したファイルか、指定した固定の名前です - doitで同じ処理方法を複数のターゲットに対して行いたい時は、サブタスクの定義という機能で行います
- doitではタスク定義を辞書型オブジェクトを返す関数を定義することで行います
doit 使用説明書: タスク定義・依存管理・task creator 辞書キー完全ガイド
- 対象: doit 0.37 系および現行公式ドキュメント(2026-09-02確認)
- 公式ドキュメントと現行 master の Task.valid_attr / Task.init を照合
1. doit の概要
doit は、処理(action)と依存関係を Python で宣言するタスクランナーです。依存ファイル、生成物、他タスク、任意の最新判定を記録し、結果が変わらないタスクを省略します。Make のような用途に加え、データ変換、文書生成、テスト、環境整備にも向きます。
1.1 インストールと確認
python -m pip install -U doit
doit --version
doit help
doit help run
doit help task
1.2 最小の dodo.py
from pathlib import Path
def write_greeting(targets):
Path(targets[0]).write_text("Hello, doit!\n", encoding="utf-8")
def task_greeting():
"""挨拶ファイルを生成する"""
return {
"actions": [write_greeting],
"targets": ["build/greeting.txt"],
"clean": True,
}
dodo.py のあるディレクトリで doit を実行します。初回は実行記号「.」、最新なら「--」が表示されます。task creator(task_ で始まる関数)はメタデータ作成時に毎回呼ばれますが、actions は選択され、かつ最新でない場合だけ実行されます。重い処理や副作用を creator 本体に置かないことが重要です。
doit # 既定タスクを実行
doit greeting # タスクを指定
doit -a greeting # 最新判定を無視して強制実行
doit clean greeting # clean を実行
doit list # タスク一覧
doit info greeting # タスク情報
1.3 task creator が返す辞書
通常は dict を return し、サブタスク群は dict を yield します。辞書はタスクそのものではなく Task オブジェクトを生成するメタデータです。actions は原則必須です。creator の通常 return では basename が実タスク名に変換され、サブタスクでは basename と name から完全名が組み立てられます。
2. 全キー一覧(重要度順)
| キー | 区分 | 値の型 | 役割 | 公式資料 |
|---|---|---|---|---|
actions |
必須 | list | tuple |
実行する処理を順番に列挙。シェルコマンド、Python callable、(callable, args, kwargs)、Action オブジェクトを指定。 | 該当箇所 |
file_dep |
最重要 | list | tuple[str | Path] |
入力ファイル。内容のシグネチャが前回成功時から変化すると再実行。単なる更新日時変更では通常再実行されない。 | 該当箇所 |
targets |
最重要 | list | tuple[str | Path] |
生成物のファイルまたはディレクトリ。存在しなければ実行。同じ target を複数タスクで共有不可。 | 該当箇所 |
task_dep |
重要 | list | tuple[str] |
先に処理させるタスク名。ワイルドカード * も使用可能。依存先が最新でも順序は保証される。 | 該当箇所 |
uptodate |
重要 | list | tuple[bool | None | str | callable | tuple] |
独自の最新判定を追加。False は常時実行、True はこの検査を通過。callable や設定値検査も使用可能。 | 該当箇所 |
clean |
重要 | True | list | tuple |
doit clean 時の処理。True は targets を削除。リストなら通常の action と同じ形式の独自処理。 | 該当箇所 |
name |
サブタスク | str(特殊時 None) |
yield する各サブタスクの識別名。完全名は basename:name。name=None は空のグループタスク定義に使用。 | 該当箇所 |
basename |
命名 | str |
task_関数名から導く既定名を上書き。通常タスク名またはサブタスク群の基底名。 | 該当箇所 |
setup |
重要 | list | tuple[str] |
対象タスクが実際に走る場合だけ、その直前に実行するセットアップタスク名。 | 該当箇所 |
teardown |
重要 | list | tuple[action] |
タスク実行後の後始末 action。setup と組み合わせて一時環境の開始・停止に使う。 | 該当箇所 |
getargs |
重要 | dict[str, tuple[str, str | None]] |
別タスクの Python action が返した辞書値を、当タスクの action のキーワード引数へ渡す。暗黙の setup 依存を作る。 | 該当箇所 |
calc_dep |
高度 | list | tuple[str] |
依存関係を実行時に計算するタスク名。計算タスクは file_dep/task_dep/uptodate/calc_dep を含む辞書を返す。 | 該当箇所 |
params |
引数 | list | tuple[dict] |
タスク固有のコマンドラインオプション定義。各辞書に name、short/long、default、type 等を指定。 | 該当箇所 |
pos_arg |
引数 | str | None |
タスク名以後に残る位置引数を受け取る Python action の引数名。値は list[str]。 | 該当箇所 |
verbosity |
表示 | None | 0 | 1 | 2 |
0: stdout/stderr を捕捉、1: stdout のみ捕捉(既定)、2: どちらも即時表示。 | 該当箇所 |
title |
表示 | callable(Task) -> str | None |
実行表示のタスク名部分を作る関数。文字列ではなく callable を指定する。 | 該当箇所 |
doc |
表示 | str | None |
doit list/help に表示する説明。未指定なら task creator の docstring の先頭非空行。 | 該当箇所 |
watch |
監視 | list | tuple[str | Path] |
自動監視プラグイン等で、file_dep 以外に変化を監視するファイル/ディレクトリ。通常 run の最新判定には加わらない。 | 該当箇所 |
meta |
拡張 | dict | None |
doit 本体が直接解釈しない任意メタデータ。独自コマンドやプラグインが利用。 | 該当箇所 |
io |
高度/内部寄り | dict | None |
action の入出力捕捉に関する IOConfig。現行実装で受理されるが公式利用説明が乏しく、通常は verbosity を使う。 | 該当箇所 |
注: subtask_of、has_subtask、loader は Task.init に存在する内部属性ですが、task creator 辞書の valid_attr には含まれず、ユーザーが返すキーではありません。dependencies、changed、task、targets は Python action に自動注入できる引数名であり、辞書キーの追加項目ではありません。
3. 主要なキーの使い方
3.1 actions - 実行内容
複数 action は上から順に実行され、途中で失敗すれば後続は実行されません。シェル文字列は shell=True、文字列/Path のリストは shell=False です。Python action は None/True/dict/str を返せば成功、False は失敗、例外はエラーです。dict は getargs 用の値として保存されます。
def convert(source, targets, mode="fast"):
# Python action はメタデータ名を引数名で受け取れる
print(f"{source} -> {targets[0]} ({mode})")
return {"output": targets[0]} # doit DB に保存
def task_actions_demo():
return {
"actions": [
"echo preparing", # shell command
(["python", "-V"]), # shell=False
(convert, ["input.txt"], {"mode": "safe"}),
],
"targets": ["output.txt"],
"verbosity": 2,
}
コマンド文字列では %(dependencies)s、%(changed)s、%(targets)s が使用できます。リスト形式のコマンドには置換が働きません。パスや空白を安全に扱う必要がある場合は、リスト形式または Python action を推奨します。
3.2 file_dep と targets - 増分実行
def task_markdown():
return {
"actions": [
"python convert.py %(dependencies)s %(targets)s"
],
"file_dep": ["source.docx", "convert.py"],
"targets": ["source.md"],
"clean": True,
}
file_dep の内容が変わる、target が存在しない、または追加の最新判定が失敗すると action が走ります。target 自体を書き換えても、存在していて入力が不変なら通常は再実行されません。パス表記は文字列として照合されるため、file.txt と ./file.txt を混在させないでください。
3.3 task_dep - タスク間の順序
def task_fetch():
return {"actions": ["python fetch.py"], "targets": ["raw.json"]}
def task_report():
return {
"actions": ["python report.py"],
"task_dep": ["fetch"],
"file_dep": ["raw.json"],
"targets": ["report.pdf"],
}
raw.json が fetch の target かつ report の file_dep なら、doit は暗黙にも順序を推論します。処理上の前提でありファイル関係ではない場合は task_dep を明示します。
3.4 uptodate - 独自の最新判定
from doit.tools import config_changed, run_once
SETTINGS = {"format": "pdf", "quality": 90}
def task_export():
return {
"actions": ["python export.py"],
"uptodate": [config_changed(SETTINGS)],
"targets": ["out.pdf"],
}
def task_always():
return {"actions": ["python show_status.py"], "uptodate": [False]}
def task_once():
return {"actions": ["python initialize.py"], "uptodate": [run_once]}
uptodate の各要素は AND 的に評価されます。callable は True(最新)、False(再実行)、None(判断を追加しない)を返します。文字列は前回 task result との比較に使われます。callable 形式は (callable, args, kwargs) も指定できます。
3.5 clean - 生成物の削除と独自後始末
from doit.task import clean_targets
def remove_cache():
print("remove application cache")
def task_build():
return {
"actions": ["python build.py"],
"targets": ["build/result.bin"],
# targets 削除に加え、独自処理も行う
"clean": [clean_targets, remove_cache],
}
clean=True は空でないディレクトリを再帰削除しません。独自 clean の Python action は dryrun 引数を宣言すると doit clean --dry-run 時にも呼ばれ、実削除を避けた表示処理を実装できます。
3.6 name と basename - サブタスク
PACKAGES = ["docling", "pymupdf4llm"]
def task_pip_upgrade():
"""指定パッケージを更新する"""
for package in PACKAGES:
yield {
"name": package,
"actions": [["python", "-m", "pip", "install", "-U", package]],
"verbosity": 2,
}
# doit pip_upgrade:docling
通常は creator 名から basename=pip_upgrade が補われます。明示する場合は各 yield 辞書に同じ basename を設定できます。グループ名だけを選択すると全サブタスクが対象になります。
3.7 setup と teardown - 実行時だけ用意する環境
def start_server(): print("server start")
def stop_server(): print("server stop")
def task_server():
return {"actions": [start_server], "teardown": [stop_server]}
def task_integration_test():
return {
"actions": ["pytest tests/integration"],
"setup": ["server"],
}
setup は通常の task_dep と異なり、integration_test が最新なら実行されません。setup タスクの teardown は、関連タスク群の終了後に呼ばれます。
3.8 getargs - タスク結果の受け渡し
def task_detect_version():
def detect():
return {"version": "2.4.1", "changed": True}
return {"actions": [detect]}
def publish(version):
print(f"publish {version}")
def task_publish():
return {
"actions": [publish],
"getargs": {"version": ("detect_version", "version")},
}
getargs のキー version は受け側 action のキーワード引数名です。値の第1要素が参照タスク名、第2要素が結果辞書のキーです。第2要素を None にすると辞書全体を渡します。グループタスクを参照すると、サブタスク名をキーとする辞書になります。戻り値は JSON 化可能な値にしてください。
3.9 calc_dep - 実行後に依存関係を追加
def task_scan_dependencies():
def scan():
files = read_manifest("manifest.txt")
return {"file_dep": files}
return {"actions": [scan]}
def task_dynamic_build():
return {
"actions": ["python build.py"],
"calc_dep": ["scan_dependencies"],
"targets": ["build.bin"],
}
依存の列挙自体が高コスト、または別タスクの生成物が必要な場合に使います。計算タスクの Python action は file_dep、task_dep、uptodate、calc_dep のうち必要なキーを持つ辞書を返します。単に creator のロードを遅らせたい場合は delayed task も検討してください。
3.10 params と pos_arg - タスク固有の CLI 引数
def run_job(level, names):
print(level, names)
def task_job():
return {
"actions": [run_job],
"params": [{
"name": "level",
"short": "l",
"long": "level",
"default": 1,
"type": int,
"help": "処理レベル",
}],
"pos_arg": "names",
"uptodate": [False],
}
# doit job --level 3 alpha beta
params の name は action の同名引数へ渡されます。pos_arg は残りの位置引数を list[str] として渡します。タスク固有引数はタスク名の後に置きます。選択肢、複数回指定、bool フラグ等の詳細は公式 task arguments を参照してください。
4. 表示・拡張用キー
4.1 doc / title / verbosity
def describe(task):
return f"RUN {task.name}: {task.targets}"
def task_visible():
return {
"actions": ["python noisy.py"],
"doc": "表示制御の例",
"title": describe,
"verbosity": 2,
}
doc は一覧・help 用、title は run 時の表示行用です。verbosity=2 はリアルタイムログが必要な長時間処理に有用です。グローバルには DOIT_CONFIG や -v でも指定できます。
4.2 watch
watch は file_dep と別の監視対象です。doit 本体から auto コマンドが分離された現在は、doit-auto1 等の監視機構が利用します。通常の doit run における依存シグネチャには使われません。サブタスクグループの name=None 定義にも指定できます。
def task_docs():
yield {"name": None, "watch": ["docs/"]}
yield {"name": "html", "actions": ["sphinx-build docs build/html"]}
4.3 meta
def task_unit_tests():
return {
"actions": ["pytest -q"],
"meta": {"tags": ["test", "fast"], "owner": "qa"},
}
meta は doit 標準の実行判断を変えません。独自 loader、command、reporter、プラグインで分類や所有者情報などを読む場合に使います。
4.4 io
io は現行 Task が受理する辞書キーで、capture 設定を内部 IOConfig に渡します。ただし公式ユーザーガイドに安定した公開設定としての説明がなく、通常の出力制御には verbosity が適切です。プラグイン/API 経由の高度用途でのみ、利用する doit バージョンのソースとテストを確認してください。
全キーを列挙するという目的から io も掲載していますが、一般的な dodo.py で積極的に使うことは推奨しません。
5. タスクの再実行要否の判定
doit はタスクを実行する前に、そのタスクが up-to-date(再実行不要) であるかを判定する。判定には主に file_dep、targets、uptodate が使用される。
-
file_dep- タスクが依存するファイルを指定する。
- doit は依存ファイルの状態を
.doit.dbに記録し、前回の正常実行時から変更されている場合にはタスクを再実行する。 - make のような単純なファイル更新日時の比較とは異なり、前回実行時に記録した状態との比較が基本となる。
-
targets- タスクによって生成されるファイルを指定する。
- 指定された target が存在しない場合、タスクは再実行が必要と判定される。
-
uptodate- 再実行の要否を追加条件によって判定する。
-
Trueならその条件については再実行不要、Falseなら再実行が必要と判定される。 - callable を指定することもでき、
task引数を受け取ればtask.file_depやtask.targetsなどを参照した独自の判定も可能である。 - 例えば、依存ファイルと target の更新日時を比較することで、make と同様の判定条件を追加できる。
file_dep、targets、uptodate などによって再実行が必要と判断されなければ、タスクの actions は実行されない。これらの判定材料を持たないタスクは、原則として指定されるたびに実行される。
なお、uptodate で独自の判定を行う場合でも、タスクが正常に実行されると doit 自身の状態管理情報は .doit.db に記録される。uptodate の callable が返した True / False 自体がそのまま保存されるわけではない。
6. 実践的な完全例
from pathlib import Path
from doit.tools import config_changed
SRC = Path("input/data.csv")
OUT = Path("build/report.txt")
CFG = {"encoding": "utf-8", "upper": True}
def make_report(dependencies, targets, upper):
text = Path(dependencies[0]).read_text(encoding="utf-8")
if upper:
text = text.upper()
Path(targets[0]).parent.mkdir(parents=True, exist_ok=True)
Path(targets[0]).write_text(text, encoding="utf-8")
return {"lines": len(text.splitlines())}
def task_report():
"""CSV からレポートを生成する"""
return {
"actions": [(make_report, [], {"upper": CFG["upper"]})],
"file_dep": [SRC],
"targets": [OUT],
"uptodate": [config_changed(CFG)],
"clean": True,
"verbosity": 2,
"meta": {"category": "document"},
}
def show_summary(lines):
print(f"{lines} lines generated")
def task_summary():
return {
"actions": [show_summary],
"getargs": {"lines": ("report", "lines")},
"uptodate": [False],
}
7. 設計上の注意とトラブルシュート
7.1 creator と action を分離する
creator 本体は doit list、info、最新判定、別タスク実行時にもロードされます。pip 更新、ファイル変換、ログ追記、ネットワークアクセスなどは必ず actions 内へ置きます。creator 側では軽量なパス列挙と辞書構築に留めます。
7.2 ログを『実行時だけ』残す
ログ書込みを Python action の末尾に置けば、最新でスキップされた場合には記録されません。処理前後の状態が必要なら action 内で取得し、処理成功後にまとめて記録します。
7.3 強制実行と状態の消去を区別する
doit -a TASK は最新判定を無視して今回実行します。doit forget TASK は保存済み依存状態を消します。doit clean は clean 処理を行います。この3つは目的が異なります。
7.4 JupyterLab からの実行
dodo.py の場所を明示し、Python の subprocess.run で外部コマンドとして呼ぶと、カレントディレクトリ変更をノートブック全体へ残さずに済みます。
from pathlib import Path
import subprocess
project = Path(r"C:\work\project")
result = subprocess.run(
["python", "-m", "doit", "pip_upgrade:docling"],
cwd=project,
text=True,
capture_output=True,
)
print(result.stdout)
print(result.stderr)
result.check_returncode()
7.5 よくある誤り
・actions をリストにせず単一文字列にする。
・file_dep と targets で同一パスの表記を混在させる。
・別タスクと同じ target を宣言する。
・Python action の引数名が params/getargs/自動注入名と一致しない。
・action の戻り値に JSON 化不能なオブジェクトを含め、getargs の保存に失敗する。
・サブタスク名の完全形 basename:name を task_dep 等で書き忘れる。
8. 公式リファレンス
Tasks - 基本、actions、命名、file_dep、targets、表示
Task dependency system - task_dep、setup、teardown、getargs、calc_dep
Passing arguments - params、pos_arg
Task manipulation commands - clean、auto/watch
More on Task creation - delayed task 等
版に関する注記: 公式サイトは継続更新されます。本書は 2026-09-02 時点の公式サイトと、doit リポジトリ master(pyproject では 0.38.0.dev0)を確認し、ユーザー環境で広く使われる 0.37 系との共通部分を中心に記述しました。io のような公開説明が限定的な項目は、その旨を明記しています。