1 //===--- IndexerMain.cpp -----------------------------------------*- C++-*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // clangd-indexer is a tool to gather index data (symbols, xrefs) from source.
11 //===----------------------------------------------------------------------===//
13 #include "CompileCommands.h"
15 #include "index/IndexAction.h"
16 #include "index/Merge.h"
17 #include "index/Ref.h"
18 #include "index/Serialization.h"
19 #include "index/Symbol.h"
20 #include "index/SymbolCollector.h"
21 #include "support/Logger.h"
22 #include "clang/Tooling/ArgumentsAdjusters.h"
23 #include "clang/Tooling/Execution.h"
24 #include "clang/Tooling/Tooling.h"
25 #include "llvm/Support/CommandLine.h"
26 #include "llvm/Support/Signals.h"
33 static llvm::cl::opt
<IndexFileFormat
>
34 Format("format", llvm::cl::desc("Format of the index to be written"),
35 llvm::cl::values(clEnumValN(IndexFileFormat::YAML
, "yaml",
36 "human-readable YAML format"),
37 clEnumValN(IndexFileFormat::RIFF
, "binary",
38 "binary RIFF format")),
39 llvm::cl::init(IndexFileFormat::RIFF
));
41 static llvm::cl::list
<std::string
> QueryDriverGlobs
{
44 "Comma separated list of globs for white-listing gcc-compatible "
45 "drivers that are safe to execute. Drivers matching any of these globs "
46 "will be used to extract system includes. e.g. "
47 "/usr/bin/**/clang-*,/path/to/repo/**/g++-*"),
48 llvm::cl::CommaSeparated
,
51 class IndexActionFactory
: public tooling::FrontendActionFactory
{
53 IndexActionFactory(IndexFileIn
&Result
) : Result(Result
) {}
55 std::unique_ptr
<FrontendAction
> create() override
{
56 SymbolCollector::Options Opts
;
57 Opts
.CountReferences
= true;
58 Opts
.FileFilter
= [&](const SourceManager
&SM
, FileID FID
) {
59 const auto F
= SM
.getFileEntryRefForID(FID
);
61 return false; // Skip invalid files.
62 auto AbsPath
= getCanonicalPath(*F
, SM
.getFileManager());
64 return false; // Skip files without absolute path.
65 std::lock_guard
<std::mutex
> Lock(FilesMu
);
66 return Files
.insert(*AbsPath
).second
; // Skip already processed files.
68 return createStaticIndexingAction(
72 std::lock_guard
<std::mutex
> Lock(SymbolsMu
);
73 for (const auto &Sym
: S
) {
74 if (const auto *Existing
= Symbols
.find(Sym
.ID
))
75 Symbols
.insert(mergeSymbol(*Existing
, Sym
));
81 std::lock_guard
<std::mutex
> Lock(RefsMu
);
82 for (const auto &Sym
: S
) {
83 // Deduplication happens during insertion.
84 for (const auto &Ref
: Sym
.second
)
85 Refs
.insert(Sym
.first
, Ref
);
89 std::lock_guard
<std::mutex
> Lock(RelsMu
);
90 for (const auto &R
: S
) {
94 /*IncludeGraphCallback=*/nullptr);
97 bool runInvocation(std::shared_ptr
<CompilerInvocation
> Invocation
,
99 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
100 DiagnosticConsumer
*DiagConsumer
) override
{
101 disableUnsupportedOptions(*Invocation
);
102 return tooling::FrontendActionFactory::runInvocation(
103 std::move(Invocation
), Files
, std::move(PCHContainerOps
), DiagConsumer
);
106 // Awkward: we write the result in the destructor, because the executor
107 // takes ownership so it's the easiest way to get our data back out.
108 ~IndexActionFactory() {
109 Result
.Symbols
= std::move(Symbols
).build();
110 Result
.Refs
= std::move(Refs
).build();
111 Result
.Relations
= std::move(Relations
).build();
117 llvm::StringSet
<> Files
;
118 std::mutex SymbolsMu
;
119 SymbolSlab::Builder Symbols
;
121 RefSlab::Builder Refs
;
123 RelationSlab::Builder Relations
;
127 } // namespace clangd
130 int main(int argc
, const char **argv
) {
131 llvm::sys::PrintStackTraceOnErrorSignal(argv
[0]);
133 const char *Overview
= R
"(
134 Creates an index of symbol information etc in a whole project.
136 Example usage for a project using CMake compile commands:
138 $ clangd-indexer --executor=all-TUs compile_commands.json > clangd.dex
140 Example usage for file sequence index without flags:
142 $ clangd-indexer File1.cpp File2.cpp ... FileN.cpp > clangd.dex
144 Note: only symbols from header files will be indexed.
147 auto Executor
= clang::tooling::createExecutorFromCommandLineArgs(
148 argc
, argv
, llvm::cl::getGeneralCategory(), Overview
);
151 llvm::errs() << llvm::toString(Executor
.takeError()) << "\n";
155 // Collect symbols found in each translation unit, merging as we go.
156 clang::clangd::IndexFileIn Data
;
157 auto Mangler
= std::make_shared
<clang::clangd::CommandMangler
>(
158 clang::clangd::CommandMangler::detect());
159 Mangler
->SystemIncludeExtractor
= clang::clangd::getSystemIncludeExtractor(
160 static_cast<llvm::ArrayRef
<std::string
>>(
161 clang::clangd::QueryDriverGlobs
));
162 auto Err
= Executor
->get()->execute(
163 std::make_unique
<clang::clangd::IndexActionFactory
>(Data
),
164 clang::tooling::ArgumentsAdjuster(
165 [Mangler
= std::move(Mangler
)](const std::vector
<std::string
> &Args
,
166 llvm::StringRef File
) {
167 clang::tooling::CompileCommand Cmd
;
168 Cmd
.CommandLine
= Args
;
169 Mangler
->operator()(Cmd
, File
);
170 return Cmd
.CommandLine
;
173 clang::clangd::elog("{0}", std::move(Err
));
176 // Emit collected data.
177 clang::clangd::IndexFileOut
Out(Data
);
178 Out
.Format
= clang::clangd::Format
;