diff --git a/CMakeLists.txt b/CMakeLists.txt index af9bcc1a..0a9e2462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ if(CLANG_LINK_CLANG_DYLIB) target_link_libraries(ccls PRIVATE clang-cpp) else() target_link_libraries(ccls PRIVATE + clangCodeGen clangIndex clangFormat clangTooling @@ -94,7 +95,7 @@ endif() if(LLVM_LINK_LLVM_DYLIB) target_link_libraries(ccls PRIVATE LLVM) else() - target_link_libraries(ccls PRIVATE LLVMOption LLVMSupport) + target_link_libraries(ccls PRIVATE LLVMOption LLVMSupport LLVMTarget LLVMX86CodeGen LLVMX86Desc LLVMX86Info) if(LLVM_VERSION_MAJOR GREATER_EQUAL 16) # llvmorg-16-init-15123-gf09cf34d0062 target_link_libraries(ccls PRIVATE LLVMTargetParser) endif() @@ -201,6 +202,7 @@ target_sources(ccls PRIVATE src/filesystem.cc src/fuzzy_match.cc src/main.cc + src/compile.cc src/indexer.cc src/log.cc src/lsp.cc diff --git a/src/compile.cc b/src/compile.cc new file mode 100644 index 00000000..fbedabfd --- /dev/null +++ b/src/compile.cc @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace clang; + +struct StoreDiags : DiagnosticConsumer { + const LangOptions *langOpts; + std::string message; + + void BeginSourceFile(const LangOptions &opts, const Preprocessor *) override { langOpts = &opts; } + void HandleDiagnostic(DiagnosticsEngine::Level level, const clang::Diagnostic &info) override { + DiagnosticConsumer::HandleDiagnostic(level, info); + const char *prefix; + switch (level) { + default: + return; + case DiagnosticsEngine::Warning: + prefix = "warning"; + break; + case DiagnosticsEngine::Error: + prefix = "error"; + break; + case DiagnosticsEngine::Fatal: + prefix = "fatal error"; + break; + } + + llvm::SmallString<64> msg; + info.FormatDiagnostic(msg); + auto &sm = info.getSourceManager(); + std::pair i = sm.getDecomposedLoc(sm.getFileLoc(info.getLocation())); + int l = (int)sm.getLineNumber(i.first, i.second), c = (int)sm.getColumnNumber(i.first, i.second); + llvm::raw_string_ostream(message) << l << ':' << c << ": " << prefix << ": " << msg << '\n'; + } +}; + +std::pair compile(llvm::StringRef source) { + int fd_in = memfd_create("a.cc", 0), fd_out = memfd_create("a.o", 0); + llvm::raw_fd_stream osIn(fd_in, true), osOut(fd_out, true); + if (fd_in < 0 || fd_out < 0) + return {"", "failed to open file"}; + osIn << source; + osIn.seek(0); + + auto fs = llvm::vfs::getRealFileSystem(); + StoreDiags dc; + char file_in[64], file_out[64]; + snprintf(file_in, sizeof(file_in), "/proc/self/fd/%d", fd_in); + snprintf(file_out, sizeof(file_out), "/proc/self/fd/%d", fd_out); + std::vector args{ + "clang", "-c", "-Wall", "-Wextra", "-xc++", file_in, "-o", file_out, "-fno-ident"}; + llvm::IntrusiveRefCntPtr diags(CompilerInstance::createDiagnostics( +#if LLVM_VERSION_MAJOR >= 20 + *fs, +#endif + new DiagnosticOptions, &dc, false)); + driver::Driver d(args[0], "x86_64", *diags, "cc", fs); + d.setCheckInputsExist(false); + std::unique_ptr comp(d.BuildCompilation(args)); + const auto &jobs = comp->getJobs(); + assert(jobs.size() == 1 && isa(*jobs.begin())); + const llvm::opt::ArgStringList &cc_args = cast(*jobs.begin()).getArguments(); + auto ci = std::make_unique(); + CompilerInvocation::CreateFromArgs(*ci, cc_args, *diags); + ci->getFrontendOpts().DisableFree = false; + + auto clang = std::make_unique(std::make_shared()); + clang->setInvocation(std::move(ci)); + clang->createDiagnostics( +#if LLVM_VERSION_MAJOR >= 20 + *fs, +#endif + &dc, false); + diags.reset(); + clang->setTarget(TargetInfo::CreateTargetInfo(clang->getDiagnostics(), clang->getInvocation().TargetOpts)); + if (!clang->hasTarget()) + return {"", "unsupported target"}; + clang->createFileManager(fs); + clang->setSourceManager(new SourceManager(clang->getDiagnostics(), clang->getFileManager(), true)); + + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86Target(); + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86TargetMC(); + + auto action = std::make_unique(); + action->BeginSourceFile(*clang, clang->getFrontendOpts().Inputs[0]); + llvm::Error e = action->Execute(); + if (e) + return {"", llvm::toString(std::move(e))}; + action->EndSourceFile(); + + auto size = (long)lseek(fd_out, 0, SEEK_END); + std::string ret(size, '\0'); + osOut.seek(0); + osOut.read(ret.data(), size); + return {std::move(ret), std::move(dc.message)}; +}