visual-studiocmakeclangllvm-clanglibtooling

How can I use LibTooling/Clang in Visual Studio?


I'm trying to use LibTooling to replace function and variable names in C-code files. Thus I downloaded llvm and followed the instructions to set it up in windows using GMake and Visual Studio 2015.

There are many tutorials (e.g. this one) which I'd like to follow, but they all assume that you are using Linux/Make, so they provide a Makefile which somehow manages the integration into the llvm source code.

What I want to do is use these examples in Visual Studio, but I don't know where to begin - can I just create a new project inside the given (llvn.sln) solution and add the code there? How do I tell Visual Studio to include the clang sources (as defined in those Makefiles I mentioned)?


Solution

  • Environment:Windows 10 X64,VS2017,CMake 3.9.3 References: https://clang.llvm.org/get_started.html

    Create a working directory,like D:\software\LLVM

    Download LLVM and Clang source code(version 6.0.0):

    http://releases.llvm.org/6.0.0/llvm-6.0.0.src.tar.xz http://releases.llvm.org/6.0.0/cfe-6.0.0.src.tar.xz

    extract them in working directory,and rename them to llvm and clang(remove version codes):

    D:\software\LLVM\llvm
    
    D:\software\LLVM\clang
    

    if you keep the version code in the folder name,then later CMake will say can't find folder "clang"

    create a build output directory under working directory,like:

    D:\software\LLVM\build-x86
    

    and for x64(only if you need x64 build):

    D:\software\LLVM\build-x64
    

    get into the build output directory:

    cd D:\software\LLVM\build-x86
    

    run cmake,for x86 build:

    cmake -DLLVM_ENABLE_PROJECTS=clang -G "Visual Studio 15 2017" -A Win32 -Thost=x64 ..\llvm
    

    for x64 build:

    cmake -DLLVM_ENABLE_PROJECTS=clang -G "Visual Studio 15 2017" -A x64 -Thost=x64 ..\llvm
    

    Note: It's better to only enable the target that your clang need to support, or the build process will be very slow. To only build the targets that your clang will support,use

    -DLLVM_TARGETS_TO_BUILD=target_list
    

    for example:

    -DLLVM_TARGETS_TO_BUILD="X86;ARM"
    

    The list of all targets seems to be in:

    D:\software\LLVM\llvm\bindings\python\llvm\disassembler.py
    

    they are:

    _targets = ['AArch64', 'ARM', 'Hexagon', 'MSP430', 'Mips', 'NVPTX', 'PowerPC', 'R600', 'Sparc', 'SystemZ', 'X86', 'XCore']
    

    So X86 seems to include both x86 and x64.

    CMake will product a VS solution file:

    D:\software\LLVM\build-x86\LLVM.sln
    

    open it with VS2017,and you only need to build the sub group "Clang libraries",my build is the default Debug build.

    Now the reference is https://kevinaboos.wordpress.com/2013/07/23/clang-tutorial-part-ii-libtooling-example/ but the code in that page is for older version of LLVM so it won't compile.

    Here is the working code with LLVM6.0.0:

    create a C++ console application in VS2017.

    get into debug/x86 configuration.

    in project properties dialog,for debug/win32 config,

    modify "Disable Specific Warnings" to:

    4146
    

    modify "Additional Include Directories" to:

    D:\software\LLVM\clang\include;D:\software\LLVM\llvm\include;D:\software\LLVM\build-x86\include;D:\software\LLVM\build-x86\tools\clang\include;%(AdditionalIncludeDirectories)
    

    modify "Additional Library Directories" to:

    D:\software\LLVM\build-x86\Debug\lib;%(AdditionalLibraryDirectories)
    

    modify "Additional Dependencies" to:

    Mincore.lib;clangAnalysis.lib;clangARCMigrate.lib;clangAST.lib;clangASTMatchers.lib;clangBasic.lib;clangCodeGen.lib;clangCrossTU.lib;clangDriver.lib;clangDynamicASTMatchers.lib;clangEdit.lib;clangFormat.lib;clangFrontend.lib;clangFrontendTool.lib;clangHandleCXX.lib;clangIndex.lib;clangLex.lib;clangParse.lib;clangRewrite.lib;clangRewriteFrontend.lib;clangSema.lib;clangSerialization.lib;clangStaticAnalyzerCheckers.lib;clangStaticAnalyzerCore.lib;clangStaticAnalyzerFrontend.lib;clangTooling.lib;clangToolingASTDiff.lib;clangToolingCore.lib;clangToolingRefactor.lib;libclang.lib;LLVMAArch64AsmParser.lib;LLVMAArch64AsmPrinter.lib;LLVMAArch64CodeGen.lib;LLVMAArch64Desc.lib;LLVMAArch64Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAMDGPUAsmParser.lib;LLVMAMDGPUAsmPrinter.lib;LLVMAMDGPUCodeGen.lib;LLVMAMDGPUDesc.lib;LLVMAMDGPUDisassembler.lib;LLVMAMDGPUInfo.lib;LLVMAMDGPUUtils.lib;LLVMAnalysis.lib;LLVMARMAsmParser.lib;LLVMARMAsmPrinter.lib;LLVMARMCodeGen.lib;LLVMARMDesc.lib;LLVMARMDisassembler.lib;LLVMARMInfo.lib;LLVMARMUtils.lib;LLVMAsmParser.lib;LLVMAsmPrinter.lib;LLVMBinaryFormat.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMBPFAsmParser.lib;LLVMBPFAsmPrinter.lib;LLVMBPFCodeGen.lib;LLVMBPFDesc.lib;LLVMBPFDisassembler.lib;LLVMBPFInfo.lib;LLVMCodeGen.lib;LLVMCore.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoMSF.lib;LLVMDemangle.lib;LLVMGlobalISel.lib;LLVMHexagonAsmParser.lib;LLVMHexagonCodeGen.lib;LLVMHexagonDesc.lib;LLVMHexagonDisassembler.lib;LLVMHexagonInfo.lib;LLVMInstCombine.lib;LLVMInstrumentation.lib;LLVMipo.lib;LLVMIRReader.lib;LLVMLanaiAsmParser.lib;LLVMLanaiAsmPrinter.lib;LLVMLanaiCodeGen.lib;LLVMLanaiDesc.lib;LLVMLanaiDisassembler.lib;LLVMLanaiInfo.lib;LLVMLinker.lib;LLVMMC.lib;LLVMMCDisassembler.lib;LLVMMCParser.lib;LLVMMipsAsmParser.lib;LLVMMipsAsmPrinter.lib;LLVMMipsCodeGen.lib;LLVMMipsDesc.lib;LLVMMipsDisassembler.lib;LLVMMipsInfo.lib;LLVMMSP430AsmPrinter.lib;LLVMMSP430CodeGen.lib;LLVMMSP430Desc.lib;LLVMMSP430Info.lib;LLVMNVPTXAsmPrinter.lib;LLVMNVPTXCodeGen.lib;LLVMNVPTXDesc.lib;LLVMNVPTXInfo.lib;LLVMObject.lib;LLVMOption.lib;LLVMPowerPCAsmParser.lib;LLVMPowerPCAsmPrinter.lib;LLVMPowerPCCodeGen.lib;LLVMPowerPCDesc.lib;LLVMPowerPCDisassembler.lib;LLVMPowerPCInfo.lib;LLVMProfileData.lib;LLVMScalarOpts.lib;LLVMSelectionDAG.lib;LLVMSparcAsmParser.lib;LLVMSparcAsmPrinter.lib;LLVMSparcCodeGen.lib;LLVMSparcDesc.lib;LLVMSparcDisassembler.lib;LLVMSparcInfo.lib;LLVMSupport.lib;LLVMSystemZAsmParser.lib;LLVMSystemZAsmPrinter.lib;LLVMSystemZCodeGen.lib;LLVMSystemZDesc.lib;LLVMSystemZDisassembler.lib;LLVMSystemZInfo.lib;LLVMTableGen.lib;LLVMTarget.lib;LLVMTransformUtils.lib;LLVMVectorize.lib;LLVMX86AsmParser.lib;LLVMX86AsmPrinter.lib;LLVMX86CodeGen.lib;LLVMX86Desc.lib;LLVMX86Disassembler.lib;LLVMX86Info.lib;LLVMX86Utils.lib;LLVMXCoreAsmPrinter.lib;LLVMXCoreCodeGen.lib;LLVMXCoreDesc.lib;LLVMXCoreDisassembler.lib;LLVMXCoreInfo.lib;
    

    because I can't find information about which library to include ,I include all libraries in D:\software\LLVM\build-x86\Debug\lib,plus Mincore.lib

    Here is a C# code to generate the lib references:

    static void buildLLVMLibList()
    {
        string s = "";
        foreach (string fn in Directory.GetFiles(
            //@"D:\software\LLVM\build-x64\Debug\lib"
            @"D:\software\LLVM\build-x86\Debug\lib"
            , "*.lib"))
        {
            s += new FileInfo(fn).Name + ";";
        }
        return;//break here
    }
    

    modify pch.h to:

    #ifndef PCH_H
    #define PCH_H
    
    // TODO: add headers that you want to pre-compile here
    // Declares clang::SyntaxOnlyAction.
    #include "clang/Frontend/FrontendActions.h"
    #include "clang/Tooling/CommonOptionsParser.h"
    #include "clang/Tooling/Tooling.h"
    // Declares llvm::cl::extrahelp.
    #include "llvm/Support/CommandLine.h"
    
    #include "clang/Driver/Options.h"
    #include "clang/AST/AST.h"
    #include "clang/AST/ASTContext.h"
    #include "clang/AST/ASTConsumer.h"
    #include "clang/AST/RecursiveASTVisitor.h"
    #include "clang/Frontend/ASTConsumers.h"
    #include "clang/Frontend/FrontendActions.h"
    #include "clang/Frontend/CompilerInstance.h"
    #include "clang/Tooling/CommonOptionsParser.h"
    #include "clang/Tooling/Tooling.h"
    #include "clang/Rewrite/Core/Rewriter.h"
    
    using namespace std;
    using namespace clang;
    using namespace clang::driver;
    using namespace clang::tooling;
    using namespace llvm;
    
    #endif //PCH_H
    

    modify main cpp file to:

    #include "pch.h"
    #include <iostream>
    
    
    
    Rewriter rewriter;
    int numFunctions = 0;
    llvm::cl::OptionCategory MyToolCategory("my-tool options");
    
    class ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> {
    private:
        ASTContext *astContext; // used for getting additional AST info
    
    public:
        explicit ExampleVisitor(CompilerInstance *CI)
            : astContext(&(CI->getASTContext())) // initialize private members
        {
            rewriter.setSourceMgr(astContext->getSourceManager(),
                astContext->getLangOpts());
        }
    
        virtual bool VisitFunctionDecl(FunctionDecl *func) {
            numFunctions++;
            string funcName = func->getNameInfo().getName().getAsString();
            if (funcName == "do_math") {
                rewriter.ReplaceText(func->getLocation(), funcName.length(), "add5");
                errs() << "** Rewrote function def: " << funcName << "\n";
            }
            return true;
        }
    
        virtual bool VisitStmt(Stmt *st) {
            if (ReturnStmt *ret = dyn_cast<ReturnStmt>(st)) {
                rewriter.ReplaceText(ret->getRetValue()->getLocStart(), 6, "val");
                errs() << "** Rewrote ReturnStmt\n";
            }
            if (CallExpr *call = dyn_cast<CallExpr>(st)) {
                rewriter.ReplaceText(call->getLocStart(), 7, "add5");
                errs() << "** Rewrote function call\n";
            }
            return true;
        }
    };
    
    
    class ExampleASTConsumer : public ASTConsumer {
    private:
        ExampleVisitor *visitor; // doesn't have to be private
    
    public:
        // override the constructor in order to pass CI
        explicit ExampleASTConsumer(CompilerInstance *CI)
            : visitor(new ExampleVisitor(CI)) // initialize the visitor
        { }
    
        // override this to call our ExampleVisitor on the entire source file
        virtual void HandleTranslationUnit(ASTContext &Context) {
            /* we can use ASTContext to get the TranslationUnitDecl, which is
                 a single Decl that collectively represents the entire source file */
            visitor->TraverseDecl(Context.getTranslationUnitDecl());
        }
    
    };
    
    
    
    class ExampleFrontendAction : public ASTFrontendAction {
    public:
    
        void EndSourceFileAction() override {
            llvm::outs() << "END OF FILE ACTION:\n";
            rewriter.getEditBuffer(rewriter.getSourceMgr().getMainFileID()).write(errs());
        }
    
        virtual std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef file) {
            return  std::unique_ptr<ASTConsumer>(new ExampleASTConsumer(&CI)); // pass CI pointer to ASTConsumer
        }
    };
    
    
    extern llvm::cl::OptionCategory MyToolCategory;
    
    int main(int argc, const char **argv) {
        // parse the command-line args passed to your code
        CommonOptionsParser op(argc, argv, MyToolCategory);
        // create a new Clang Tool instance (a LibTooling environment)
        ClangTool Tool(op.getCompilations(), op.getSourcePathList());
    
        // run the Clang Tool, creating a new FrontendAction (explained below)
        int result = Tool.run(newFrontendActionFactory<ExampleFrontendAction>().get());
    
        errs() << "\nFound " << numFunctions << " functions.\n\n";
        // print out the rewritten source code ("rewriter" is a global var.)
    
        std::cin.get();
        return result;
    }
    

    Then create a testC++ file,like:

    D:\LLVM\TestSource.cpp
    

    its content is like:

    void do_math(int *x) {
        *x += 5;
    }
    
    int main(void) {
        int result = -1, val = 4;
        do_math(&val);
        return result;
    }
    

    then in project properties,modify "Command Arguments" to:

    "D:\LLVM\TestSource.cpp"
    

    Then the project will build and debug successfully.

    For debug/x64 configuration,modify project properties to:

    include path:

    D:\software\LLVM\clang\include;D:\software\LLVM\llvm\include;D:\software\LLVM\build-x64\include;D:\software\LLVM\build-x64\tools\clang\include;%(AdditionalIncludeDirectories)
    

    library path:

    D:\software\LLVM\build-x64\Debug\lib;%(AdditionalLibraryDirectories)
    

    library(different from x86 library):

    Mincore.lib;clangAnalysis.lib;clangARCMigrate.lib;clangAST.lib;clangASTMatchers.lib;clangBasic.lib;clangCodeGen.lib;clangCrossTU.lib;clangDriver.lib;clangDynamicASTMatchers.lib;clangEdit.lib;clangFormat.lib;clangFrontend.lib;clangFrontendTool.lib;clangHandleCXX.lib;clangIndex.lib;clangLex.lib;clangParse.lib;clangRewrite.lib;clangRewriteFrontend.lib;clangSema.lib;clangSerialization.lib;clangStaticAnalyzerCheckers.lib;clangStaticAnalyzerCore.lib;clangStaticAnalyzerFrontend.lib;clangTooling.lib;clangToolingASTDiff.lib;clangToolingCore.lib;clangToolingRefactor.lib;gtest.lib;libclang.lib;LLVMAArch64AsmParser.lib;LLVMAArch64AsmPrinter.lib;LLVMAArch64CodeGen.lib;LLVMAArch64Desc.lib;LLVMAArch64Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAMDGPUAsmParser.lib;LLVMAMDGPUAsmPrinter.lib;LLVMAMDGPUCodeGen.lib;LLVMAMDGPUDesc.lib;LLVMAMDGPUDisassembler.lib;LLVMAMDGPUInfo.lib;LLVMAMDGPUUtils.lib;LLVMAnalysis.lib;LLVMARMAsmParser.lib;LLVMARMAsmPrinter.lib;LLVMARMCodeGen.lib;LLVMARMDesc.lib;LLVMARMDisassembler.lib;LLVMARMInfo.lib;LLVMARMUtils.lib;LLVMAsmParser.lib;LLVMAsmPrinter.lib;LLVMBinaryFormat.lib;LLVMBitReader.lib;LLVMBitWriter.lib;LLVMBPFAsmParser.lib;LLVMBPFAsmPrinter.lib;LLVMBPFCodeGen.lib;LLVMBPFDesc.lib;LLVMBPFDisassembler.lib;LLVMBPFInfo.lib;LLVMCodeGen.lib;LLVMCore.lib;LLVMDebugInfoCodeView.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMDemangle.lib;LLVMGlobalISel.lib;LLVMHexagonAsmParser.lib;LLVMHexagonCodeGen.lib;LLVMHexagonDesc.lib;LLVMHexagonDisassembler.lib;LLVMHexagonInfo.lib;LLVMInstCombine.lib;LLVMInstrumentation.lib;LLVMipo.lib;LLVMIRReader.lib;LLVMLanaiAsmParser.lib;LLVMLanaiAsmPrinter.lib;LLVMLanaiCodeGen.lib;LLVMLanaiDesc.lib;LLVMLanaiDisassembler.lib;LLVMLanaiInfo.lib;LLVMLinker.lib;LLVMMC.lib;LLVMMCDisassembler.lib;LLVMMCParser.lib;LLVMMipsAsmParser.lib;LLVMMipsAsmPrinter.lib;LLVMMipsCodeGen.lib;LLVMMipsDesc.lib;LLVMMipsDisassembler.lib;LLVMMipsInfo.lib;LLVMMSP430AsmPrinter.lib;LLVMMSP430CodeGen.lib;LLVMMSP430Desc.lib;LLVMMSP430Info.lib;LLVMNVPTXAsmPrinter.lib;LLVMNVPTXCodeGen.lib;LLVMNVPTXDesc.lib;LLVMNVPTXInfo.lib;LLVMObject.lib;LLVMOption.lib;LLVMPowerPCAsmParser.lib;LLVMPowerPCAsmPrinter.lib;LLVMPowerPCCodeGen.lib;LLVMPowerPCDesc.lib;LLVMPowerPCDisassembler.lib;LLVMPowerPCInfo.lib;LLVMProfileData.lib;LLVMScalarOpts.lib;LLVMSelectionDAG.lib;LLVMSparcAsmParser.lib;LLVMSparcAsmPrinter.lib;LLVMSparcCodeGen.lib;LLVMSparcDesc.lib;LLVMSparcDisassembler.lib;LLVMSparcInfo.lib;LLVMSupport.lib;LLVMSymbolize.lib;LLVMSystemZAsmParser.lib;LLVMSystemZAsmPrinter.lib;LLVMSystemZCodeGen.lib;LLVMSystemZDesc.lib;LLVMSystemZDisassembler.lib;LLVMSystemZInfo.lib;LLVMTableGen.lib;LLVMTarget.lib;LLVMTransformUtils.lib;LLVMVectorize.lib;LLVMX86AsmParser.lib;LLVMX86AsmPrinter.lib;LLVMX86CodeGen.lib;LLVMX86Desc.lib;LLVMX86Disassembler.lib;LLVMX86Info.lib;LLVMX86Utils.lib;LLVMXCoreAsmPrinter.lib;LLVMXCoreCodeGen.lib;LLVMXCoreDesc.lib;LLVMXCoreDisassembler.lib;LLVMXCoreInfo.lib;
    

    disable specific warning:

    4146
    

    command argument:

    "D:\LLVM\TestSource.cpp"
    

    The most important thing is you have to move the code

    rewriter.getEditBuffer(rewriter.getSourceMgr().getMainFileID()).write(errs());
    

    in the above reference from the main function to ExampleFrontendAction::EndSourceFileAction or there will be weird access exceptions.