ようへいの日々精進XP

よかろうもん

make で実行可能なタスク一覧をデフォルトで出力させたいヨ

おはげようございます。朝の三分雑ハッキングのお時間です。

追記

以下のようにスクリプトをモジュールとして利用することでワンライナーっぽいことが出来るようになった。ちょっとうれしい。

#!/usr/bin/env python

import re
import sys

def foo():
    for line in sys.stdin:
        match = re.match(r'^([a-z\-]*):(?:.*)#\s*(.*)$', line)
        if match:
            print match.group(1).ljust(12) + '....... ' +match.group(2)

以下のように...。

% python -c 'from make_tasks import foo; foo()' < Makefile
all         ....... Execute all tasks
hello       ....... Execute hello(タスクの説明)
world       ....... Execute world(タスクの説明)

Makefile には以下のように組み込んでおく。

default:
        @echo Makefile for MyTask
        @echo
        @echo Usage: make [task]
        @echo
        @echo Tasks:
        @python -c 'from make_tasks import foo; foo()' < Makefile

all: hello world # Execute all tasks

hello: # Execute hello(タスクの説明)
        @./hello.sh && { echo "success!" ; exit 0; } || { echo "failure!" | ./notification.sh ; exit 1; }

world: hello # Execute world(タスクの説明)
        @./world.sh && { echo "success!" | ./notification.sh ; exit 0; } || { echo "failure!" | ./notification.sh ; exit 1; }

make を叩くと以下のようになる。

% make
Makefile for MyTask

Usage: make [task]

Tasks:
all         ....... Execute all tasks
hello       ....... Execute hello(タスクの説明)
world       ....... Execute world(タスクの説明)

おお、「一応」ワンライナーいけた。


tl;dr

幾つかのスクリプトをタスクとして make コマンドで実行するようにしてみたけど、make だけ叩いた時には実行可能なタスク一覧を表示するようにしたいなと思ってトライした。(rake -T でタスク一覧がザザって出力されるようにしたい!)


既に(参考)

以下のように実装されている方がいらっしゃったので、これを参考にさせて頂いて Python を利用して実装してみた。

gist.github.com

上記の例では Rubyワンライナーが炸裂しているが、流石に Pythonワンライナーは個人的に敷居が高かったのが悔やまれる。


memo

以下のような Makefile で進める

default:
        @echo Makefile for MyTask
        @echo
        @echo Usage: make [task]
        @echo
        @echo Tasks:
        @./make_tasks.py < Makefile

all: hello world # Execute all tasks

hello: # Execute hello(タスクの説明)
        @./hello.sh && { echo "success!" ; exit 0; } || { echo "failure!" | ./notification.sh ; exit 1; }

world: hello # Execute world(タスクの説明)
        @./world.sh && { echo "success!" | ./notification.sh ; exit 0; } || { echo "failure!" | ./notification.sh ; exit 1; }

各タスク名の後ろの # 以降にタスクの説明を記載する。

make だけ叩いた場合の挙動

  • 一番冒頭に定義しているタスクが実行される
  • これを利用する

default タスク

参考にさせて頂いて make だけ叩かれた際に実行されるタスクを default タスクとして以下のように定義。

default:
        @echo Makefile for MyTask
        @echo
        @echo Usage: make [task]
        @echo
        @echo Tasks:
        @./make_tasks.py < Makefile

タスク一覧出力を担うのは最終行の ./make_tasks.py < Makefile の部分。雑な Python スクリプトMakefile 自身を読み込ませている。

雑な Python スクリプト

#!/usr/bin/env python

import re
import sys

for line in sys.stdin:
        match = re.match(r'^([a-z\-]*):(?:.*)#\s*(.*)$', line)
        if match:
                print match.group(1).ljust(12) + '....... ' +match.group(2)
  • rematch メソッドを利用して標準入力からタスク名だけを抽出
  • マッチしない行は None となる
  • マッチした行の内容は group メソッドで出力することが出来る
  • また、group メソッドに添字をつけてマッチした文字列を抽出することも出来る

実際にスクリプトだけを実行すると以下のように出力される。

% ./make_tasks.py < Makefile
all         ....... Execute all tasks
hello       ....... Execute hello(タスクの説明)
world       ....... Execute world(タスクの説明)

また、マッチしない行も出力するようにしてみると以下のように出力される。

% ./make_tasks.py < Makefile
None
None
None
None
None
None
None
None
<_sre.SRE_Match object at 0x101045580>
None
<_sre.SRE_Match object at 0x101045580>
None
None
<_sre.SRE_Match object at 0x101045608>
None

マッチしている部分がオブジェクトとして出力されている。

実際に make だけを叩いてみる

満を持して叩いてみる。

% make
Makefile for MyTask

Usage: make [task]

Tasks:
all         ....... Execute all tasks
hello       ....... Execute hello(タスクの説明)
world       ....... Execute world(タスクの説明)

おお、それっぽい感じになった。


まとめ

  • make だけ叩くと冒頭のタスクが実行される
  • タスクの一覧は自前で実装する必要がありそう(make 自体にそのようなオプションがあるか否かは未確認)
  • Python正規表現を扱う場合には re ライブラリが便利
  • 正規表現のパターンを書く場合にはパターンの前に r を付けておくと良い(re.match(r'^([a-z\-]*):(?:.*)#\s*(.*)$', line)
  • ワンライナーで書けなかったのが残念