きりかノート 3冊め

おあそびプログラミング

7月のCocoa勉強会の発表「Three20のlintについて調べてみた」

書くのすっかり忘れてた。前々回、7月のCocoa勉強会の自分の発表です。

スライドはこちら。

前置き

Three20は多数の開発者が参加していることもあって、コードのスタイルを検証・訂正する"lint"機能を提供しています。Objective-C/Cocoaでその手のツールはめずらしく、どのように実装しているか興味があったので調べてみました。

きっかけ

Three20のMLをながめてたら「lintツールつくって、それで修正かけてみたよ」(メール"Three20 Lint Tool"pull request)というのがありました。

で、その修正の数がまあすごいたくさんなんです。で、これはどうやってるのかなと興味もったわけです。

Three20のスタイルガイド

Three20にはコーディングスタイルガイドがあって

  • Copyrightの表記
  • importの順番
  • 1行の長さ
  • インデント/空白/改行
  • init/deallocは先頭に

などなどが決められています。

これらが守られていないコードがあるとき、警告を出したり、訂正するのがlintの役割になります。コミットやpull requestする前にそれくらいは確認しとけってことですね。

試してみよう

となると、実際にどんな感じが試してみたいですよね。

  1. チェックアウトする
  2. てきとうに変更する
  3. Xcodeでビルドする

警告がでましたね。コンパイル時の警告と同じようにコードへジャンプもできます。

実現のしくみ

ざっとまとめると以下のようになっています。

  • lintはpythonスクリプトで、ビルド時にスクリプトを実行している。
  • lint機能の実装はふつうにテキスト処理(行に分割→チェック処理にかける)
  • 決まった形式で出力すると、Xcodeはそれをコンパイラの警告として扱うことを利用
  • プロジェクトのファイルの判定は.xcodeprojファイルのコメントを分析

clangのライブラリを使えるpythonのライブラリがあったように記憶していたので、そのへんを期待していたのですが、そんなことはありませんでした。

lintはpythonスクリプト

src/scripts/lintが実体になります。どう見てもpythonスクリプトですね。-dまたは--delintオプションで訂正を行ってくれるようです。

lintの実装

カナメのlint部分を見てみましょう。

 if len(line) > maxlinelength:
     did_lint_cleanly = False
     nwarnings = nwarnings + 1

     # This is not something we can fix with the delinter.
     if isdelinting:
         logger.error('I don\'t know how to split this line up.')
     else:
         logger.error('Line length > %d'% maxlinelength)

これは1行の長さですね。

 if re.search(r'^@property\(', line):
     nwarnings = nwarnings + 1
     if isdelinting:
         line = line.rstrip(' \t')
         nwarningsfixed = nwarningsfixed + 1
     else:
         did_lint_cleanly = False
         logger.error('Must be a space after the @property declarator')

これは@propertyの後にスペースを空けろてことですね。

 # Trailing whitespace
 if re.search('[ \t]+$', line):
     nwarnings = nwarnings + 1
     if isdelinting:
         line = line.rstrip(' \t')
         nwarningsfixed = nwarningsfixed + 1
     else:
         did_lint_cleanly = False
         logger.error('Trailing whitespace')

これは行末のスペースですね。

って、ぜんぶテキストの処理で構文解析とかぜんぜんしないのかー。あと、行単位の処理していて、複数行に渡る・・・なんかは今のところこのlintではサポートされていないようです。

Xcodeへの表示

決まった形式で文字列を出力すると、Xcodeコンパイラの警告・エラーと同様に扱うってやつですね。ユニットテストを提供するテスティングフレームワークなんかでもよく使われている手法です。

プロジェクトのファイル

lintそのものはファイルごとに処理するものですから、対象のファイルを特定する必要があります。

あたりかなあと想像しつつ、見てみました。src/scripts/Pbxproj.pyのget_built_headersとget_built_sourcesがその実装になります。

って、コメントから抽出してるのかよ!!いや、確かにじゅうぶんに機能するんですけど、、、その発想はなかったっす。

感想

なんだかすごくフツーというか、ちょっと肩透かしな感じになってしまいました。

他にもObjective-Cで似たようなことをしているところはないかと探してみたのですが、google-toolbox for macWebkitでもそういうものはありませんでした。これらのプロジェクトではコードレビューを重視していて、人間が確認するから、そもそもへんなパッチを書くヤツは少ないだろう、みたいな考えがあるのかなあと思いました。勝手な推測ですが。

Javaなんかはこの手のツールが発展している印象がありますが、Objective-Cは大規模とかエンタープライズとは縁遠いので、そういう開発スタイルのちがいもあるんでしょうね。