WindowsでClangのPython bindingを使う



C/C++の関数一覧や変数一覧を拾いたい。
でも自分でコンパイラの字句解析・構文解析を行うのはしんどい。

そんなときに、Clangならの[AST(抽象構文木)](https://ja.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E6%A7%8B%E6%96%87%E6%9C%A8)を取得できて、
さらにPythonで簡単に利用できるようにPython bindingが用意されているので使ってみました。

ただ、Windows環境下で使うにはいろいろとセットアップが必要だったので以下にまとめます。
※Linux環境や Cygwin環境下で使えば簡単にセットアップはできるけど、環境的にWindows側でやりたかったので…

# 確認環境・ツール

## 使用するツール

+ Windows 10 (64bit)
+ Build Tools for Visual Studio 2019
+ LLVM 11.0.0 (64bit)
+ Python 3.9.0 (64bit)

## セットアップ手順

### Build Tools for Visual Studio 2019のインストール

LLVMのPre-Built版は、msvcのインクルードファイルやライブラリなどを参照してビルドを行うため、Build Tools for Visual Studioのインストールを行う。

1. Installer のダウンロード
    + [Microsoft Visual Studioのダウンロードページ](https://visualstudio.microsoft.com/ja/downloads/)にアクセス
    + **Visual Stduio 2019のツール** をクリックして展開
    + 「Build Tools for Visual Studio 2019」の**ダウンロード**をクリックしてタウンロードする
2. インストール
    + Installerを起動
    + 「ワークロード」から**C++ Build Tools**にチェックを入れて、**インストール**をクリック


### LLVM のインストール

ClangはLLVMの一部としてリリースされているため、LLMVをインストールします。

1. Installer のダウンロード
    + [LLVMのDownload Page](https://releases.llvm.org/download.html) へアクセス
    + 任意のVersionの **Pre-Built Binaries:** の **Windows (64-bit)** をクリックしてダウンロード
2. インストール
    + Installerを起動
    + **次へ**をクリック
    + ライセンス契約書で**同意する**をクリック
    + Install Optionsで、**Add LLVM to the system PATH for all PATH**を選択して、**次へ**をクリック
    + インストール先の画面でインストール先が`C:\Program Files\LLVM`であることを確認して、**次へ**
    + スタートメニューフォルダの選択画面で特に変更せずに **インストール**をクリック
    + インストール完了したら **完了**をクリック


### Pythonのインストール

※細かいVersionやインストール時の設定は個人の好みで好きに変更して問題ないかと

1. Installer のダウンロード
    + [Pythonの公式HP](https://www.python.org/) へアクセス
    + **Downloads** をクリックしてダウンロードページへ
    + 任意のReleaseVersionの**Download**リンクをクリック (こちらでは、Python 3.9.0を選択)
    + **Windows x86-64 executable installer**をクリックしてダウンロード
2. インストール
    + Installerを起動
    + **Add Python 3.9 to PATH** にチェックを入れて、 **Customize installation**をクリック
    + Optinal Featuersの画面では特に変更せず(すべてチェック入り)に、 **Next**をクリック
    + Advanced Optionsの画面で、**Install for all users** にチェックを入れて、**Install**をクリック
        + インストール先パスを個人ユーザ側ではなく、`C:\Program Files\Python39` にしたかったので…
    + インストール完了まで待って、**Close**をクリック

### Clang Python bindingのインストール

1. pipにて Clang Python binding を取得する
    + コマンドプロンプトで `pip install clang` を実行する
2. インストール後の確認
    + コマンドプロンプトで `python`と入力してインタラクティブモードへ
    + `import clang` と入力して何もエラーが出なければOK
    + **Ctrl+Z** で、インタラクティブモードから抜ける

# 実際にClang Python bindingを使ってみる

## C/C++コードの準備

ASTの出力確認用なのでかなり適当なコード
includeしていると、include先のファイルも解析してしまい、結果が見難くなるので、それすらないコードを準備

```sample.c
int a = 2;
int b = 10;

int functionA(int x)
{
	return a * x + b;
}
```

## Pythonコードを作成

```clang_ast_test.py
import platform
import clang.cindex

# Clangのライブラリパス設定
if (platform.system()=='Windows'):
    clang.cindex.Config.set_library_path(r'C:\Program Files\LLVM\bin')

# ノードを再帰的に出力する関数
def dump_node(cursor, indent):
    print("%s[KIND:%s] { extent: { File:%s, start:{ l:%d, c:%d }, end {l:%d, c:%d } } , spelling:%s, type:{ kind:%s, spelling:%s } }" % (
            '  ' * indent,
            cursor.kind.name,
            cursor.extent.start.file,
            cursor.extent.start.line,
            cursor.extent.start.column,
            cursor.extent.end.line,
            cursor.extent.end.column,
            cursor.spelling,
            cursor.type.kind.name,
            cursor.type.spelling
            ))
    # 子ノードを表示
    for child in cursor.get_children():
        dump_node(child, indent + 1)


# Clangを使ってソースコードを構文解析
file_path = 'sample.c'
index = clang.cindex.Index.create()
translation_unit = index.parse(file_path)

# 構文解析のエラー表示
for diag in translation_unit.diagnostics:
    print(diag)

# 構文解析結果を表示
dump_node(translation_unit.cursor, 0)
```

## Pythonコードの実行結果

作ったPythonコードを実行したらこんな感じで出力。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538393/cb49e1ad-219e-8a0d-cbb8-f0e98b0f92bb.png)



## 補足：Clangのコマンドラインでast-dumpの結果を見てみる

コマンドプロンプトで `clang －Xclang -ast-dump -c  <ソースコードファイル名>`  と実行でastのツリーが確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538393/47f4cb71-b4b4-e720-406e-da1da18f119a.png)