2016年9月25日日曜日

xcode 8.0 で clang-format を使えるようにする方法

xcode 8.0 でプラグイン周りの仕様がだいぶ変わってしまったようで、今まで外部プラグイン(Alcatraz)で入れていたclang-formatが使えなくなった(参考)らしい。という訳で、Alcatrazを使わずにxcode 8.0に新しいプラグイン方式に対応したclang-formatを探してみて、幾らか方法が見つかったのですが、どれも面倒くさそう...

まぁ、Alcatraz自体、元から面倒くさかったんですよね。

xcodeをアップデートする都度、一時的に使えなくなったりするので。

という訳で、
・xcodeのアップデートに動じず
・commit時に漏れなく簡単に全ソースにclang-formatを実行でき
・楽であること
という全ての条件を満たす方式を考えることにしました。

その結果、
「普通にmakefileで書くのが一番楽では?」
という結論に至る。

という訳で、私のxcode projectでmakefileを使ってclang-formatの実行を自動化したやり方を書いておきます。

まず、xcode projectのディレクトリ直下に以下のようなシェルを作成

■format.sh
#!/bin/sh
echo formatting: $1
clang-format -assume-filename=.clang-format <$1 >.work
diff .work $1 >/dev/null
if [ $? -eq 1 ]
then
cat .work > $1
fi

\rm -f .work


このシェルがやっていることは、
1. 引数に指定されたソース に clang-format を掛けたソース(.work)を作成
2. 元ソース と .work を比較
3. 差分があれば元ソースの内容を .work の内容に書き換える
ということ。
毎回無条件でclang-formatを掛けてしまうと、差分ビルドに時間が掛かってしまうので、clang-formatの結果変化があるソースのみを書き換える感じです。

そして、同ディレクトリに以下のような makefile を作成

■makefile
HEADER = $(shell for file in `find . -name *.h`;do echo $$file; done)
C_SOURCE = $(shell for file in `find . -name *.c`;do echo $$file; done)
CPP_SOURCE = $(shell for file in `find . -name *.cpp`;do echo $$file; done)
OBJC_SOURCE = $(shell for file in `find . -name *.m`;do echo $$file; done)
SOURCES = $(HEADER) $(C_SOURCE) $(CPP_SOURCE) $(OBJC_SOURCE)

all:
    for SOURCE in $(SOURCES); do make format SRC=$$SOURCE; done

format:
    @sh format.sh $(SRC)


上記のmakefileでは現在のカレントディレクトリ(find .)から拡張子 .h .c .m のファイルを検索して見つけたリスト(SOURCES)を作り、そのリスト内のファイルをひとつづつ先程のシェル(format.sh)に指定して実行するという感じのものです。

あとは、git commit をする前に一発makeを叩けば、全ソースのコードフォーマットが掛けられるという感じになります。(findの所はプロジェクトによっては若干書き換えが必要になることがあるかもしれませんが)

今開発中の Invader Block 6 のプロジェクトでこの makefile を叩いてみた結果は以下のような感じです。
$ make
for SOURCE in IBLOCK6/AppDelegate.h IBLOCK6/ball.h IBLOCK6/bird.h IBLOCK6/bomb.h IBLOCK6/bonus.h IBLOCK6/check.h IBLOCK6/dot.h IBLOCK6/draw.h IBLOCK6/enemy.h IBLOCK6/ft.h IBLOCK6/game.h IBLOCK6/hanabi.h IBLOCK6/hit.h IBLOCK6/laser.h IBLOCK6/player.h IBLOCK6/replay.h IBLOCK6/stage.h IBLOCK6/teki.h IBLOCK6/utility.h IBLOCK6/vgs2.h IBLOCK6/VGSLayer.h IBLOCK6/VGSView.h IBLOCK6/ViewController.h IBLOCK6/wall.h IBLOCK6/ball.c IBLOCK6/bird.c IBLOCK6/bomb.c IBLOCK6/bonus.c IBLOCK6/check.c IBLOCK6/dot.c IBLOCK6/draw.c IBLOCK6/enemy.c IBLOCK6/game.c IBLOCK6/game_lv1.c IBLOCK6/game_lv2.c IBLOCK6/game_lv3.c IBLOCK6/game_opening.c IBLOCK6/game_ranking.c IBLOCK6/game_result.c IBLOCK6/game_title.c IBLOCK6/hanabi.c IBLOCK6/hit.c IBLOCK6/laser.c IBLOCK6/player.c IBLOCK6/replay.c IBLOCK6/stage.c IBLOCK6/teki.c IBLOCK6/utility.c IBLOCK6/vgs2api.c IBLOCK6/vgs2i.c IBLOCK6/wall.c IBLOCK6/AppDelegate.m IBLOCK6/main.m IBLOCK6/VGSLayer.m IBLOCK6/VGSView.m IBLOCK6/ViewController.m; do make format SRC=$SOURCE; done
formatting: IBLOCK6/AppDelegate.h
formatting: IBLOCK6/ball.h
formatting: IBLOCK6/bird.h
formatting: IBLOCK6/bomb.h
formatting: IBLOCK6/bonus.h
formatting: IBLOCK6/check.h
formatting: IBLOCK6/dot.h
formatting: IBLOCK6/draw.h
formatting: IBLOCK6/enemy.h
formatting: IBLOCK6/ft.h
formatting: IBLOCK6/game.h
formatting: IBLOCK6/hanabi.h
formatting: IBLOCK6/hit.h
formatting: IBLOCK6/laser.h
formatting: IBLOCK6/player.h
formatting: IBLOCK6/replay.h
formatting: IBLOCK6/stage.h
formatting: IBLOCK6/teki.h
formatting: IBLOCK6/utility.h
formatting: IBLOCK6/vgs2.h
formatting: IBLOCK6/VGSLayer.h
formatting: IBLOCK6/VGSView.h
formatting: IBLOCK6/ViewController.h
formatting: IBLOCK6/wall.h
formatting: IBLOCK6/ball.c
formatting: IBLOCK6/bird.c
formatting: IBLOCK6/bomb.c
formatting: IBLOCK6/bonus.c
formatting: IBLOCK6/check.c
formatting: IBLOCK6/dot.c
formatting: IBLOCK6/draw.c
formatting: IBLOCK6/enemy.c
formatting: IBLOCK6/game.c
formatting: IBLOCK6/game_lv1.c
formatting: IBLOCK6/game_lv2.c
formatting: IBLOCK6/game_lv3.c
formatting: IBLOCK6/game_opening.c
formatting: IBLOCK6/game_ranking.c
formatting: IBLOCK6/game_result.c
formatting: IBLOCK6/game_title.c
formatting: IBLOCK6/hanabi.c
formatting: IBLOCK6/hit.c
formatting: IBLOCK6/laser.c
formatting: IBLOCK6/player.c
formatting: IBLOCK6/replay.c
formatting: IBLOCK6/stage.c
formatting: IBLOCK6/teki.c
formatting: IBLOCK6/utility.c
formatting: IBLOCK6/vgs2api.c
formatting: IBLOCK6/vgs2i.c
formatting: IBLOCK6/wall.c
formatting: IBLOCK6/AppDelegate.m
formatting: IBLOCK6/main.m
formatting: IBLOCK6/VGSLayer.m
formatting: IBLOCK6/VGSView.m
formatting: IBLOCK6/ViewController.m

2016年9月1日木曜日

開発状況2(IB6)

ステージ2をもりもり作成中。
Invader Block variatio 6 のステージ2は Battle Marine 仕様です。
ゲーム内容については、だいたい旧Battle Marineを去就した形になっています。敵の種類やグラフィックは流用。
プログラム的な部分は完全にゼロから作り直していて、コンボの爽快さなどを追求。

今週夏休みなので今日中ぐらいにステージ2を完成させるつもりですが、やや遅れ気味。

ステージ1(Invader Block仕様)からステージ2に進むまでの演出を凝らせていたら、作り始めるまでに結構時間が掛かってしまいました。

それ以上に、このタイミングでデレステを始めてしまい、それに時間が取られた感が否めない。
本当はコーヒーブレイク用に始めたのですがね。
ソシャゲって体力回復待ちがあるから、体力が尽きるまでプレイして、回復するまでの間に開発。そして、回復したらまたプレイみたいな風にすれば割と良い感じなんじゃないかと。
しかし、実際には無料石を回収し、石を使って(ガチャには回さず)体力回復してプレイを延々と繰り返し、そのコーヒーブレイクにIB6の開発を進めていた感が否めない。
夏休みはあと4日(木、金、土、日)で、土曜は予定があるから、流石に木、金は石を使った回復は自重するなどの縛りを入れねば。