После долгого перерыва я решил вернуться к попыткам использования clang API по нескольким причинам. Во-первых, clang API за это время продвинулся довольно далеко и стал "мощнее", так что хочется попробовать новые версии. Во-вторых, в старых статьях используется более ранние версии clang, а мне регулярно задают вопросы типа "почему у меня не компилируется/линкуется", поэтому хочется и обновить свои знания и добавить статьям актуальности. В-третьих, за пять лет внятных примеров с использованием clang так и не появилось, надо исправить такую несправедливость. Ну и в конце концов меня не оставляет надежда реализовать одну идею со статическим анализом кода, о которой, может быть, напишу позже.
Итак, попробуем написать и собрать минимальный проект для clang API на платформе Linux, используя последнюю стабильную версию llvm и clang. На момент написания статьи это версия 3.7.0.
Предыдущая статья
Сборка и установка clang
Так уж повелось, что если вы хотите использовать clang API, то вам не удастся установить нужные пакеты из репозиториев дистрибутива Linux. Выход здесь только один - собрать clang вручную. Однако здесь тоже есть подводный камень - даже если у вас в системе уже есть llvm, и (о, чудо) он нужной вам версии, то собрать отдельно только clang у вас не получится, это просто не предусмотрено разработчиками. Поэтому нужно будет полностью собрать из исходников и llvm и clang.
На сайте clang есть инструкция по сборке. Но если вы будете копипастить и выполнять оттуда команды, то вы получите не стабильную сборку, а какую-то текущую версию продукта, еще находящуюся в разработке и толком не проверенную. Чтобы избежать дальнейших проблем с "сырым" продуктом, я советую брать именно стабильную версию. Для этого вместо скачивания исходников из папки trunk, нужно взять исходники с меткой конкретной версии (в моем случае это 3.7.0):
например, было
svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
стало
svn co http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_370/final llvm
svn co http://llvm.org/svn/llvm-project/cfe/tags/RELEASE_370/final clang
Кроме этого я советую устанавливать собранные llvm и clang в отдельную папку, а не в /usr. Это позволит избежать конфликтов с "родными" версиями, поставляемыми с дистрибутивом, или какими-то другими, если вы хотите, чтобы у вас было несколько разных версий clang в системе. У себя я установил всё это в папку $HOME/opt/llvm
.
Итак, опуская проверку требований к установке, набор команд по скачиванию исходников, сборке и установке в сухом остатке будет такой:
> mkdir ~/clang-build > cd ~/clang-build > svn co http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_370/final llvm > cd ~/clang-build/llvm/tools > svn co http://llvm.org/svn/llvm-project/cfe/tags/RELEASE_370/final clang > cd ~/clang-build/llvm/tools/clang/tools > svn co http://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_370/final extra > cd ~/clang-build/llvm/projects > svn co http://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_370/final compiler-rt > cd ~/clang-build/ > mkdir build > cd ~/clang-build/build > cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX:PATH=$HOME/opt/llvm ../llvm > make > make install
После успешного выполнения всех этих команд в папке $HOME/opt/llvm
будут находится папки bin, include, lib и share.
Теперь можно проверить всё ли правильно вы собрали и установили:
> $HOME/opt/llvm/bin/llvm-config --version 3.7.0 > $HOME/opt/llvm/bin/clang --version clang version 3.7.0 (tags/RELEASE_370/final) Target: x86_64-unknown-linux-gnu Thread model: posix
Пишем код testclang.cpp
Код минимального проекта не простой, а очень простой:
#include "clang/Tooling/Tooling.h" int main () { auto astUnit = clang::tooling::buildASTFromCode("struct C {};"); return 0; }
Сохраним это в файле testclang.cpp
.
Пишем Makefile
LLVMBIN=$(HOME)/opt/llvm/bin LLVMCONF=$(LLVMBIN)/llvm-config CXXFLAGS += -g3 CXXFLAGS += $(shell $(LLVMCONF) --cxxflags) LIBS += $(shell $(LLVMCONF) --ldflags) \ -Wl,--start-group \ $(shell $(LLVMCONF) --libs) \ -lclangAnalysis \ -lclangAST \ -lclangASTMatchers \ -lclangBasic \ -lclangCodeGen \ -lclangDriver \ -lclangFrontend \ -lclangEdit \ -lclangIndex \ -lclangLex \ -lclangParse \ -lclangRewrite \ -lclangSema \ -lclangSerialization \ -lclangTooling \ -Wl,--end-group LIBS += $(shell $(LLVMCONF) --system-libs) all: $(CXX) $(CXXFLAGS) -o ./a.out testclang.cpp $(LIBS)
На что нужно обратить внимание:
- нужно получить ключи компиляции и линковки запуском llvm-config с параметрами --cxxflags и --ldflags соответственно
- ключи компиляции содержат "-std=c++11", так что код будет компилироваться по стандарту C++11
- получить список линкуемых библиотек llvm запуском llvm-config с параметром --libs
- вручную добавить список линкуемых библиотек clang (можно добавить все библиотеки clang, хуже не будет)
- для разрешения циклических зависимостей llvm и clang нужно использовать ключи линковки start-group/end-group (см. статью об этом)
- в КОНЦЕ списка линкуемых библиотек добавить системные библиотеки, возвращаемые командой '
llvm-config --system-libs
' (список библиотек может различаться на разных платформах) - и последнее, но не менее важное - обратите внимание, что все ключи линковки собраны в переменную LIBS и добавлены ПОСЛЕ имени компилируемого файла.
Строим
> make g++ -g3 -I/home/user/opt/llvm/include -fPIC -fvisibility-inlines-hidden -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wno-maybe-uninitialized -Wno-comment -std=c++11 -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -o ./a.out testclang.cpp -L/home/user/opt/llvm/lib -Wl,--start-group -lLLVMLTO -lLLVMObjCARCOpts -lLLVMLinker -lLLVMBitWriter -lLLVMIRReader -lLLVMXCoreDisassembler -lLLVMXCoreCodeGen -lLLVMXCoreDesc -lLLVMXCoreInfo -lLLVMXCoreAsmPrinter -lLLVMSystemZDisassembler -lLLVMSystemZCodeGen -lLLVMSystemZAsmParser -lLLVMSystemZDesc -lLLVMSystemZInfo -lLLVMSystemZAsmPrinter -lLLVMSparcDisassembler -lLLVMSparcCodeGen -lLLVMSparcAsmParser -lLLVMSparcDesc -lLLVMSparcInfo -lLLVMSparcAsmPrinter -lLLVMPowerPCDisassembler -lLLVMPowerPCCodeGen -lLLVMPowerPCAsmParser -lLLVMPowerPCDesc -lLLVMPowerPCInfo -lLLVMPowerPCAsmPrinter -lLLVMNVPTXCodeGen -lLLVMNVPTXDesc -lLLVMNVPTXInfo -lLLVMNVPTXAsmPrinter -lLLVMMSP430CodeGen -lLLVMMSP430Desc -lLLVMMSP430Info -lLLVMMSP430AsmPrinter -lLLVMMipsDisassembler -lLLVMMipsCodeGen -lLLVMMipsAsmParser -lLLVMMipsDesc -lLLVMMipsInfo -lLLVMMipsAsmPrinter -lLLVMHexagonDisassembler -lLLVMHexagonCodeGen -lLLVMHexagonDesc -lLLVMHexagonInfo -lLLVMCppBackendCodeGen -lLLVMCppBackendInfo -lLLVMBPFCodeGen -lLLVMBPFDesc -lLLVMBPFInfo -lLLVMBPFAsmPrinter -lLLVMARMDisassembler -lLLVMARMCodeGen -lLLVMARMAsmParser -lLLVMARMDesc -lLLVMARMInfo -lLLVMARMAsmPrinter -lLLVMAMDGPUCodeGen -lLLVMAMDGPUAsmParser -lLLVMAMDGPUUtils -lLLVMAMDGPUDesc -lLLVMAMDGPUInfo -lLLVMAMDGPUAsmPrinter -lLLVMAArch64Disassembler -lLLVMAArch64CodeGen -lLLVMAArch64AsmParser -lLLVMAArch64Desc -lLLVMAArch64Info -lLLVMAArch64AsmPrinter -lLLVMAArch64Utils -lLLVMMIRParser -lLLVMAsmParser -lLLVMLibDriver -lLLVMOption -lLLVMDebugInfoPDB -lLLVMTableGen -lLLVMOrcJIT -lLLVMLineEditor -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMX86Desc -lLLVMMCDisassembler -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMDebugInfoDWARF -lLLVMPasses -lLLVMipo -lLLVMVectorize -lLLVMInterpreter -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMCodeGen -lLLVMTarget -lLLVMScalarOpts -lLLVMProfileData -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMInstCombine -lLLVMInstrumentation -lLLVMTransformUtils -lLLVMipa -lLLVMMC -lLLVMAnalysis -lLLVMCore -lLLVMSupport -lclangAnalysis -lclangAST -lclangASTMatchers -lclangBasic -lclangCodeGen -lclangDriver -lclangFrontend -lclangEdit -lclangIndex -lclangLex -lclangParse -lclangRewrite -lclangSema -lclangSerialization -lclangTooling -Wl,--end-group -lpthread -ldl -lz
...фигасе, у него там библиотек O_o
У меня без проблем собралось на Ubuntu 15 с g++ версии 4.8.3.
Заключение
Скачали исходники llvm и clang. Собрали и инсталлировали. Написали минимальный под с использованием clang API. Собрали его.
Всё. Удачи.
Исходный код
Исходники можно взять на GitHub.
Комментариев нет:
Отправить комментарий