はじめに
以前こんな記事を書きました。
emacs flymakeでSwiftの構文チェック
この記事で紹介した方法では、プロジェクトに含まれる構文チェック対象でない他のソースファイルの情報(クラス名その他、共有する名前空間に存在するシンボル群)をコンパイラが認識できないため、エラーが大量に出てしまいます。
つまり構文チェックのswiftコマンドラインにはプロジェクトの全ソースを記述する必要があります、
(あと、flymakeではMakefileを書かないと行けなくて面倒なので、ついでにflycheckを使うようにしたいです。)
そこで、プロジェクトファイル(.pbxproj)の情報を用いてxcodeと同等のコマンドラインを生成することを目指しました。
xctool
xcodeと同等のコマンドラインの情報を得るため、facebookが公開しているxctoolを使用することにしました。
xctoolは、homebrewなら
$ brew instal xctool
でインストール出来ます。
xctoolはビルド記録を出力形式を選択したり、カスタマイズできるreporterプラグインという仕組みを持っていて、このプラグインのうち、json-compilation-database
を使うと、純粋なSwiftソースファイルのコンパイルコマンドラインが得られます。
Xcodeのプロジェクトの直下で、
$ xctool -workspace ./${PROJECT}.xcworkspace -scheme ${PROJECT} -reporter json-compilation-database -sdk iphonesimulator build
を実行すると、標準出力にビルド記録が出力されます。結構な量になるので注意してください。
JSONの内容は、
[
{
"command" : "<コンパイルコマンドライン>",
"file" : "<対象ファイル>",
"directory" : "<ディレクトリ>"
...
}
]
となっているので、このうち、配列名の任意の辞書のcommandキーの値を取り出して、構文解析用のコマンドになるよう修正します。directoryにはビルドディレクトリが記されているので、これを参照すれば、ユニットテストプロジェクトや、cocoapodsのビルドに用いているコマンドラインを除外できます。
ここで実行されるコンパイルコマンドラインはこのような形式になっています。
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c プロジェクトソースファイル群 -primary-file コンパイル対象 -target 他のコマンドラインオプション
改造するポイントとしては、
-
-frontend
の後ろに構文解析のみを行う-parse
オプションを付ける -
-primary-file
の後ろ、対象ファイルを書き換える
です。注意すべきは-primary-file
の位置が-c
と-target
の間のどこに置かれるかが不定な点です。
※文末にJSONファイルから構文解析用のswiftコマンドラインを生成するpythonスクリプトを貼っているので適宜改造してご使用ください。
このスクリプトを実行して作ったシェルスクリプトを実行可能パーミッションを付けて、flycheckに設定すればOKです。
flycheck設定
(flycheck-define-checker swift
"Flycheck plugin for for Apple's Swift programming language."
:command ("<your-bin-directory>/compile_swift"
source-original)
:error-patterns
((error line-start (file-name) ":" line ":" column ": "
"error: " (message) line-end)
(warning line-start (file-name) ":" line ":" column ": "
"warning: " (message) line-end))
:modes swift-mode)
...
(add-hook 'swift-mode-hook
'(lambda()
(add-to-list 'flycheck-checkers 'swift)
...
)
)
課題
- 複数のプロジェクトで同時並行で作業する場合に、構文チェックコマンドを切り替えるのが面倒
- プロジェクトにソースファイルが追加されたら毎回構文チェックコマンドを手動で生成し直すのが面倒
- これは、.pbxprojと構文チェックコマンドのタイムスタンプを比較して自動で実行するとか、
- gitのhookをつかって.pbxprojがコミットされた時点で再生成するとかで解決できそう
JSONから構文チェックスクリプトを生成するPythonスクリプト(python3)
#!/usr/bin/env python
import sys
import json
import re
import os.path
def main():
sources=[]
with open( sys.argv[1] ) as f:
result = json.load(f)
for l in result:
line = l["command"]
dir = l["directory"]
if os.path.basename(dir) == 'Pods':
continue
primary_file = re.search("-primary-file ([^ ]+) ",line)
if primary_file:
srcs = re.search("-c (.+) -target",line)
list = []
for src in srcs.group(1).split(' '):
if src == '-primary-file':
continue
sources.append(src)
print(primary_file.group(1),file=sys.stderr)
prefix = (os.path.basename(primary_file.group(1)).split('.'))[0]
replaced = re.sub("-frontend", '-frontend -parse',line)
replaced = re.sub("-c (.+) -target", '-c ${SRCS} -primary-file $1 -target',replaced)
replaced = re.sub(prefix, '${PRIMARY_PREFIX}',replaced)
cmd = """\
#!/bin/bash
SRCS="%s"
PRIMARY=`basename $1`
PRIMARY_PREFIX=${PRIMARY%%.swift}
%s""" % (' '.join(sources), replaced)
print(cmd)
break
if __name__ == '__main__':
main()