目次: LLVM
準備が終わりましたらClang/LLVMをプログラムから呼びましょう。
int main(int argc, char *argv[])
{
bool success;
clang::CompilerInstance CI;
clang::CompilerInvocation &build = CI.getInvocation();
// 引数の配列を作成する
std::vector<const char*> vec_args;
vec_args.push_back("-I/usr/include/c++/10");
vec_args.push_back("-I/usr/include/x86_64-linux-gnu/c++/10");
vec_args.push_back("-I/usr/include/c++/10/backward");
vec_args.push_back("-I/usr/lib/llvm-11/lib/clang/11.0.1/include");
vec_args.push_back("-I/usr/include/x86_64-linux-gnu");
vec_args.push_back("-I/usr/include");
vec_args.push_back("-I/path/to/llvm-project/_install/include");
// エラーメッセージを出力するために使われるクラス
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID = new clang::DiagnosticIDs();
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagOpts = new clang::DiagnosticOptions();
clang::TextDiagnosticBuffer *diagBuffer = new clang::TextDiagnosticBuffer();
clang::DiagnosticsEngine diags(diagID, diagOpts, diagBuffer);
CI.createDiagnostics(diagBuffer, false);
// コンパイラ呼び出し用のインスタンスを作成する
llvm::ArrayRef<const char*> ref_args(vec_args.data(), vec_args.data() + vec_args.size());
success = clang::CompilerInvocation::CreateFromArgs(build, ref_args, diags);
// コンパイラフロントエンドのオプション設定
// 入力ソースコード: test.cpp
// 出力ソースコード: test.preproc.cpp
const char *source_file = "test.cpp";
const char *preproc_file = "test.preproc.cpp";
clang::FrontendOptions &fe = build.getFrontendOpts();
clang::InputKind ik = clang::InputKind(clang::Language::CXX);
clang::FrontendInputFile fif = clang::FrontendInputFile(source_file, ik);
fe.Inputs.clear();
fe.Inputs.push_back(fif);
fe.OutputFile.assign(preproc_file);
// プリプロセスのオプション設定
// 言語: C++11
clang::PreprocessorOptions &po = build.getPreprocessorOpts();
clang::LangOptions *la = build.getLangOpts();
llvm::Triple triple = llvm::Triple();
build.setLangDefaults(*la, ik, triple, po.Includes, clang::LangStandard::lang_cxx11);
// 下記のようにオプションの一部だけ変えることもできる
//la->CPlusPlus = true;
//la->CPlusPlus11 = true;
// プリプロセスのオプション
// コメント、定義済みマクロなどは出力しない
clang::PreprocessorOutputOptions &poo = build.getPreprocessorOutputOpts();
poo.ShowCPP = true;
poo.ShowComments = false;
poo.ShowLineMarkers = false;
poo.ShowMacros = false;
poo.ShowMacroComments = false;
poo.RewriteIncludes = false;
// プリプロセス実行(失敗したらエラーログを出力する)
clang::PrintPreprocessedAction Preprocess;
success = CI.ExecuteAction(Preprocess);
if (!success) {
get_build_log(diagBuffer, (CI.hasSourceManager()) ? &CI.getSourceManager() : nullptr);
}
}
残念ながらこの呼び出し方が正解とは断言できません。探した限りではどう呼び出すべきか書かれたドキュメントも見当たりませんでした。上記の例はpoclを参考にしており、大きな間違いはないはずですが……。何かやらかしていたら教えていただけると嬉しいです。
動作確認はLLVM 12で行いました。他のバージョンだとAPIの引数などが変わっているので、ビルドすら通らないと思います。LLVMの困ったところですね……。
上記のサンプルでは引数で -Iオプションを使ってインクルードパスを指定します。インクルードパスは頑張ってヘッダファイルがある場所を調べても良いですが、おそらく同じ名前のヘッダが複数の場所にあって混乱すると思いますから、PCで動作しているClang++ から拝借するのが簡単です。
$ clang++ test.cpp -v Debian clang version 11.0.1-2 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/10 ... #include "..." search starts here: #include <...> search starts here: /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward /usr/lib/llvm-11/lib/clang/11.0.1/include /usr/include/x86_64-linux-gnu /usr/include End of search list. ...
いろいろなメッセージが出力されますが、インクルードパスは "search starts here:" の辺りに書かれています。出力は特に捻りはなくディレクトリ名そのものですので、頭に -Iを足せばオプションの出来上がりです。
プリプロセスを実行します。テスト用のプログラムは下記のとおりです。
#include <iostream>
int main(int argc, char *argv[])
{
// This is comment
std::cout << "Hello, world!!" << std::endl;
}
$ make $ ./clang_test
ファイル名などは完全に決め打ちのため引数は必要ありません。実行に成功するとプリプロセス後のソースコードtest.preproc.cppが作成されているはずです。
namespace std
{
typedef long unsigned int size_t;
typedef long int ptrdiff_t;
typedef decltype(nullptr) nullptr_t;
}
...
static ios_base::Init __ioinit;
}
int main(int argc, char *argv[])
{
std::cout << "Hello, world!!" << std::endl;
}
私の環境で実行したところ27,000行くらいあるファイルになりました。たった1つしかヘッダをincludeしてないのに凄まじい行数に展開されます。コメントは消えていますが、オプションを変更すれば残すこともできます。PreprocessorOutputOptionsのShowComments = trueにすると残ります。
$ g++ test.preproc.cpp $ ./a.out Hello, world!!
プリプロセス後のソースコードをg++ などに渡すとコンパイル可能なので、おそらく変な出力にはなっていないでしょう。
< | 2021 | > | ||||
<< | < | 06 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | - | - | - |
合計:
本日:
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2023.
Powered by PHP 8.2.15.
using GD bundled (2.1.0 compatible)(png support.)