我自己经历了创建一个使用的程序的过程
Clang 作为一个库,我想我应该发布我自己的工作 Makefile。
关键要素:
-
它使用从下载的二进制发行版https://releases.llvm.org/download.html而不是
安装通过apt-get
所以我可以准确地控制
使用的是什么版本。
-
就像OP一样,我正在使用(GNU)make
而不是cmake
以尽量减少正在发生的事情的神秘性。
-
它使用llvm-config
程序以获得大部分所需的
编译和链接选项。具体来说,llvm-config --cxxflags
获取预处理器和编译选项,以及llvm-config --ldflags --system-libs
获取链接器选项。
Clang+LLVM 可以静态或动态链接,
独立于其他库的链接方式。
For 静态链接:
For 动态链接:
-
只需要链接libclang-cpp.so
得到所有的
Clang 和 LLVM。
-
链接速度大约是原来的四倍,生成的二进制文件是
小多了。
-
缺点是libclang-cpp.so
必须可以在
运行时间(自然)。
为了完整起见,我在 Linux Mint 20.1、x86_64 上使用 GCC-9.3.0。
我的生成文件:
# clang-as-lib/Makefile
# Attempt to link with clang as a library.
# Originally based on:
# https://stackoverflow.com/questions/59888374/using-clang-as-a-library-in-c-project
# Default target.
all:
.PHONY: all
# ---- Configuration ----
# Installation directory from a binary distribution.
# Has five subdirectories: bin include lib libexec share.
# Downloaded from: https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz
CLANG_LLVM_INSTALL_DIR = $(HOME)/opt/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04
# Link with clang statically?
#
# If 1, then both clang and llvm are linked statically. Linking takes
# about 3 seconds, and the resulting binary is 48 MB.
#
# If 0, then both are obtained dynamically from libclang-cpp.so.
# Linking takes about 0.7s, and the binary is about 2.7 MB. But the .so
# file (which is about 176 MB) must be available at run time.
LINK_CLANG_STATICALLY = 1
# ---- llvm-config query results ----
# Due to using the ':=' operator (rather than '='), these queries are
# done exactly once for each 'make' invocation.
# Program to query the various LLVM configuration options.
LLVM_CONFIG := $(CLANG_LLVM_INSTALL_DIR)/bin/llvm-config
# C++ compiler options to ensure ABI compatibility.
LLVM_CXXFLAGS := $(shell $(LLVM_CONFIG) --cxxflags)
# Set of LLVM libraries to link with, as -l flags, when linking
# statically. There are 163 of them in clang+llvm-14.0.0.
LLVM_LIBS := $(shell $(LLVM_CONFIG) --libs)
# Directory containing the clang library files, both static and dynamic.
LLVM_LIBDIR := $(shell $(LLVM_CONFIG) --libdir)
# Other flags needed for linking, whether statically or dynamically.
LLVM_LDFLAGS_AND_SYSTEM_LIBS := $(shell $(LLVM_CONFIG) --ldflags --system-libs)
# ---- Compiler options ----
# C++ compiler.
CXX = g++
# Compiler options, including preprocessor options.
CXXFLAGS =
# Without optimization, adding -g increases compile time by ~20%.
#CXXFLAGS += -g
# Without -g, this increases compile time by ~10%. With -g -O2, the
# increase is ~50% over not having either.
#CXXFLAGS += -O2
CXXFLAGS += -Wall
# Silence a warning about a multi-line comment in DeclOpenMP.h.
CXXFLAGS += -Wno-comment
# Get llvm compilation flags.
CXXFLAGS += $(LLVM_CXXFLAGS)
# Linker options.
LDFLAGS =
ifeq ($(LINK_CLANG_STATICALLY),1)
# Set of clang libraries to link with. This list was obtained through
# trial and error.
LDFLAGS += -lclangTooling
LDFLAGS += -lclangFrontendTool
LDFLAGS += -lclangFrontend
LDFLAGS += -lclangDriver
LDFLAGS += -lclangSerialization
LDFLAGS += -lclangCodeGen
LDFLAGS += -lclangParse
LDFLAGS += -lclangSema
LDFLAGS += -lclangStaticAnalyzerFrontend
LDFLAGS += -lclangStaticAnalyzerCheckers
LDFLAGS += -lclangStaticAnalyzerCore
LDFLAGS += -lclangAnalysis
LDFLAGS += -lclangARCMigrate
LDFLAGS += -lclangRewrite
LDFLAGS += -lclangRewriteFrontend
LDFLAGS += -lclangEdit
LDFLAGS += -lclangAST
LDFLAGS += -lclangLex
LDFLAGS += -lclangBasic
LDFLAGS += -lclang
# *After* clang libs, the llvm libs.
LDFLAGS += $(LLVM_LIBS)
else # LINK_CLANG_STATICALLY==0
# Pull in clang+llvm via libclang-cpp.so, which has everything, but is
# only available as a dynamic library.
LDFLAGS += -lclang-cpp
# Arrange for the compiled binary to search the libdir for that library.
# Otherwise, one can set the LD_LIBRARY_PATH envvar before running it.
# Note: the -rpath switch does not work on Windows.
LDFLAGS += -Wl,-rpath=$(LLVM_LIBDIR)
endif
# Get the needed -L search path, plus things like -ldl.
LDFLAGS += $(LLVM_LDFLAGS_AND_SYSTEM_LIBS)
# ---- Recipes ----
# Compile a C++ source file.
%.o: %.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $<
# Executable.
all: FindClassDecls.exe
FindClassDecls.exe: FindClassDecls.o
$(CXX) -g -Wall -o $@ $^ $(LDFLAGS)
# Run it.
.PHONY: run
run: FindClassDecls.exe
./FindClassDecls.exe "namespace n { namespace m { class C {}; } }"
.PHONY: clean
clean:
$(RM) *.o *.exe
# EOF
FindClassDecls.cpp(来自RAVFrontendAction.html):
// FindClassDecls.cpp
// https://clang.llvm.org/docs/RAVFrontendAction.html
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Tooling.h"
using namespace clang;
class FindNamedClassVisitor
: public RecursiveASTVisitor<FindNamedClassVisitor> {
public:
explicit FindNamedClassVisitor(ASTContext *Context)
: Context(Context) {}
bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
if (Declaration->getQualifiedNameAsString() == "n::m::C") {
FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getBeginLoc());
if (FullLocation.isValid())
llvm::outs() << "Found declaration at "
<< FullLocation.getSpellingLineNumber() << ":"
<< FullLocation.getSpellingColumnNumber() << "\n";
}
return true;
}
private:
ASTContext *Context;
};
class FindNamedClassConsumer : public clang::ASTConsumer {
public:
explicit FindNamedClassConsumer(ASTContext *Context)
: Visitor(Context) {}
virtual void HandleTranslationUnit(clang::ASTContext &Context) {
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
}
private:
FindNamedClassVisitor Visitor;
};
class FindNamedClassAction : public clang::ASTFrontendAction {
public:
virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance &Compiler, llvm::StringRef InFile)
{
return std::make_unique<FindNamedClassConsumer>(&Compiler.getASTContext());
}
};
int main(int argc, char **argv) {
if (argc > 1) {
clang::tooling::runToolOnCode(std::make_unique<FindNamedClassAction>(), argv[1]);
}
}
// EOF