背景
- MacでCソースの構文解析を行いたくて、libclangで解析することに
- libclang(C言語)で解析用ソースを書くのは大変そう
- C#でlibclangを使用できるClangSharpがNuGetで公開されているのを発見
-
公式で公開されているビルド済みの
libclang.dylib
だとDllNotFoundException
が発生して使えない
原因と解決策
原因はlibclang.dylib
がx86_64(64bit)
向けにビルドされているのに対して、Xamarin Studioでビルドして生成される実行ファイルがi386(32bit)
向けになっているため、アーキテクチャが異なると怒られる
Xamarin Studio は現状x86_64
向けの実行ファイルは生成できないようなので、clangのソースコードをビルドしてi386
向けのlibclang.dylib
を生成する。
またソースに標準で付いているmake
環境でi386
向けのバイナリをビルドしようとしたところ途中でビルドエラーが発生したため、Cmake
+ ninja
1でビルドする。
環境
- Mac OSX 10.10 Yosemite
- Xamarin Studio 5.7.1
- clang 3.6
clangビルド
- LLVMのSVNリポジトリから最新版ソースをチェックアウト
$ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
$ cd llvm/tools
$ svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
$ cd ../../
$ cd llvm/tools/clang/tools
$ svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk extra
$ cd ../../../../
$ cd llvm/projects
$ svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt
$ cd ../../
-
Cmake
をインストール
$ cd llvm
$ git clone git://cmake.org/stage/cmake.git
$ cd cmake
$ git checkout next
$ ./bootstrap
$ make
$ sudo make install
-
ninja
をインストール
$ cd llvm
$ git clone https://github.com/martine/ninja.git
$ cd ninja
$ git checkout release
$ ./bootstrap.py
$ sudo cp ninja /usr/bin/
$ mkdir llvm/build
$ cd llvm/build
$ cmake -G Ninja ../llvm -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-arch i386" -DCMAKE_CXX_FLAGS="-arch i386"
$ ninja
-
llvm/build/lib
以下にlibclang.3.6.dylib
が生成される
使い方
- Xamarin Studioを起動して、新規C# コンソールプロジェクトを作成
- メニューの
プロジェクト
->Add NuGet Packages...
を開く -
clang.sharp
で検索し、Add Package
でインストール - 実行フォルダと同じディレクトリに
libclang.3.6.dylib
をコピー -
libclang.dylib
にリネーム - 解析用のソースを記述する
main.c
# include <stdio.h>
int main(void)
{
printf("Hello, World!");
}
Program.cs
using System;
using ClangSharp;
namespace ClangNet
{
class MainClass
{
/// <summary>
/// Main Method
/// </summary>
/// <param name="args">The command-line arguments.</param>
public static void Main(string[] args)
{
/* Create Index */
var index = new Index(true, true);
/* Clang Arguments */
var arg = new string[1]{ "" };
/* Create Translation Units */
var tu = index.CreateTranslationUnit("src/main.c", arg, null, TranslationUnitFlags.DetailedPreprocessingRecord);
/* Creation Success */
if(tu != null)
{
/* Get Translation Unit Cursor */
var cursor = tu.Cursor;
/* Visit Children */
cursor.VisitChildren(VisitChildrenCallback);
/* Dispose Translation Unit */
tu.Dispose();
}
/* Dispose Index */
index.Dispose();
}
/// <summary>
/// Visit Children
/// </summary>
/// <returns>Child Visit Result</returns>
/// <param name="cursor">Cursor</param>
/// <param name="parent">Parent</param>
public static Cursor.ChildVisitResult VisitChildrenCallback(Cursor cursor, Cursor parent)
{
/* Dump Cursor Kind & Cursor Spelling */
Console.WriteLine("{0} : {1}", cursor.Kind, cursor.Spelling);
/* Visit Recursively */
return Cursor.ChildVisitResult.Recurse;
}
}
}
実行結果
MacroDefinition : __llvm__
MacroDefinition : __clang__
MacroDefinition : __clang_major__
~ 省略 ~
FunctionDecl : main
CompoundStmt :
CallExpr : printf
UnexposedExpr : printf
DeclRefExpr : printf
UnexposedExpr :
UnexposedExpr :
StringLiteral : "Hello, World!"