xref: /llvm-project/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp (revision b43cfa7e45dfd252dcf8de6a753558e698a216d2)
1e975a473SJulie Hockett //===-- ClangDocMain.cpp - ClangDoc -----------------------------*- C++ -*-===//
2e975a473SJulie Hockett //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e975a473SJulie Hockett //
7e975a473SJulie Hockett //===----------------------------------------------------------------------===//
8e975a473SJulie Hockett //
9b7ecf1c1SKazuaki Ishizaki // This tool for generating C and C++ documentation from source code
10e975a473SJulie Hockett // and comments. Generally, it runs a LibTooling FrontendAction on source files,
11e975a473SJulie Hockett // mapping each declaration in those files to its USR and serializing relevant
12e975a473SJulie Hockett // information into LLVM bitcode. It then runs a pass over the collected
13e975a473SJulie Hockett // declaration information, reducing by USR. There is an option to dump this
14e975a473SJulie Hockett // intermediate result to bitcode. Finally, it hands the reduced information
15e975a473SJulie Hockett // off to a generator, which does the final parsing from the intermediate
16e975a473SJulie Hockett // representation to the desired output format.
17e975a473SJulie Hockett //
18e975a473SJulie Hockett //===----------------------------------------------------------------------===//
19e975a473SJulie Hockett 
20d0f9a872SJulie Hockett #include "BitcodeReader.h"
21d0f9a872SJulie Hockett #include "BitcodeWriter.h"
22e975a473SJulie Hockett #include "ClangDoc.h"
23e78f3018SJulie Hockett #include "Generators.h"
24d0f9a872SJulie Hockett #include "Representation.h"
25e975a473SJulie Hockett #include "clang/AST/AST.h"
26e975a473SJulie Hockett #include "clang/AST/Decl.h"
27e975a473SJulie Hockett #include "clang/ASTMatchers/ASTMatchFinder.h"
28e975a473SJulie Hockett #include "clang/ASTMatchers/ASTMatchersInternal.h"
29e975a473SJulie Hockett #include "clang/Driver/Options.h"
30e975a473SJulie Hockett #include "clang/Frontend/FrontendActions.h"
316ab28e8cSDiego Astiazaran #include "clang/Tooling/AllTUsExecution.h"
32e975a473SJulie Hockett #include "clang/Tooling/CommonOptionsParser.h"
33e975a473SJulie Hockett #include "clang/Tooling/Execution.h"
34e975a473SJulie Hockett #include "clang/Tooling/Tooling.h"
35e975a473SJulie Hockett #include "llvm/ADT/APFloat.h"
36ac68cab9SJulie Hockett #include "llvm/Support/CommandLine.h"
37d0f9a872SJulie Hockett #include "llvm/Support/Error.h"
38e975a473SJulie Hockett #include "llvm/Support/FileSystem.h"
39e2d45770SDiego Astiazaran #include "llvm/Support/Mutex.h"
40e975a473SJulie Hockett #include "llvm/Support/Path.h"
41e975a473SJulie Hockett #include "llvm/Support/Process.h"
42e975a473SJulie Hockett #include "llvm/Support/Signals.h"
436ab28e8cSDiego Astiazaran #include "llvm/Support/ThreadPool.h"
44e975a473SJulie Hockett #include "llvm/Support/raw_ostream.h"
456ab28e8cSDiego Astiazaran #include <atomic>
467b8c7e02SBrett Wilson #include <mutex>
47e975a473SJulie Hockett #include <string>
48e975a473SJulie Hockett 
49e975a473SJulie Hockett using namespace clang::ast_matchers;
50e975a473SJulie Hockett using namespace clang::tooling;
51e975a473SJulie Hockett using namespace clang;
52e975a473SJulie Hockett 
53e975a473SJulie Hockett static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
54e975a473SJulie Hockett static llvm::cl::OptionCategory ClangDocCategory("clang-doc options");
55e975a473SJulie Hockett 
563550da79SDiego Astiazaran static llvm::cl::opt<std::string>
573550da79SDiego Astiazaran     ProjectName("project-name", llvm::cl::desc("Name of project."),
583550da79SDiego Astiazaran                 llvm::cl::cat(ClangDocCategory));
593550da79SDiego Astiazaran 
608fb7074dSDiego Astiazaran static llvm::cl::opt<bool> IgnoreMappingFailures(
618fb7074dSDiego Astiazaran     "ignore-map-errors",
628fb7074dSDiego Astiazaran     llvm::cl::desc("Continue if files are not mapped correctly."),
638fb7074dSDiego Astiazaran     llvm::cl::init(true), llvm::cl::cat(ClangDocCategory));
648fb7074dSDiego Astiazaran 
65e975a473SJulie Hockett static llvm::cl::opt<std::string>
66e975a473SJulie Hockett     OutDirectory("output",
67e975a473SJulie Hockett                  llvm::cl::desc("Directory for outputting generated files."),
68e975a473SJulie Hockett                  llvm::cl::init("docs"), llvm::cl::cat(ClangDocCategory));
69e975a473SJulie Hockett 
70e975a473SJulie Hockett static llvm::cl::opt<bool>
71eb50a2e8SJulie Hockett     PublicOnly("public", llvm::cl::desc("Document only public declarations."),
72eb50a2e8SJulie Hockett                llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
73eb50a2e8SJulie Hockett 
74229c63b0SJulie Hockett static llvm::cl::opt<bool> DoxygenOnly(
75229c63b0SJulie Hockett     "doxygen",
76229c63b0SJulie Hockett     llvm::cl::desc("Use only doxygen-style comments to generate docs."),
77229c63b0SJulie Hockett     llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
78229c63b0SJulie Hockett 
79acd35f6cSDiego Astiazaran static llvm::cl::list<std::string> UserStylesheets(
80acd35f6cSDiego Astiazaran     "stylesheets", llvm::cl::CommaSeparated,
81acd35f6cSDiego Astiazaran     llvm::cl::desc("CSS stylesheets to extend the default styles."),
82acd35f6cSDiego Astiazaran     llvm::cl::cat(ClangDocCategory));
83acd35f6cSDiego Astiazaran 
84f14ad744SPeterChou1 static llvm::cl::opt<std::string> UserAssetPath(
85f14ad744SPeterChou1     "asset",
86f14ad744SPeterChou1     llvm::cl::desc("User supplied asset path to "
87f14ad744SPeterChou1                    "override the default css and js files for html output"),
88f14ad744SPeterChou1     llvm::cl::cat(ClangDocCategory));
89f14ad744SPeterChou1 
90665e9676SDiego Astiazaran static llvm::cl::opt<std::string> SourceRoot("source-root", llvm::cl::desc(R"(
91665e9676SDiego Astiazaran Directory where processed files are stored.
92665e9676SDiego Astiazaran Links to definition locations will only be
93665e9676SDiego Astiazaran generated if the file is in this dir.)"),
94665e9676SDiego Astiazaran                                              llvm::cl::cat(ClangDocCategory));
95665e9676SDiego Astiazaran 
96665e9676SDiego Astiazaran static llvm::cl::opt<std::string>
97665e9676SDiego Astiazaran     RepositoryUrl("repository", llvm::cl::desc(R"(
98665e9676SDiego Astiazaran URL of repository that hosts code.
99665e9676SDiego Astiazaran Used for links to definition locations.)"),
100665e9676SDiego Astiazaran                   llvm::cl::cat(ClangDocCategory));
101665e9676SDiego Astiazaran 
102d0f9a872SJulie Hockett enum OutputFormatTy {
103ac68cab9SJulie Hockett   md,
104d0f9a872SJulie Hockett   yaml,
105671bac74SJulie Hockett   html,
106d0f9a872SJulie Hockett };
107d0f9a872SJulie Hockett 
108ac68cab9SJulie Hockett static llvm::cl::opt<OutputFormatTy>
109ac68cab9SJulie Hockett     FormatEnum("format", llvm::cl::desc("Format for outputted docs."),
110ac68cab9SJulie Hockett                llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml",
111ac68cab9SJulie Hockett                                            "Documentation in YAML format."),
112ac68cab9SJulie Hockett                                 clEnumValN(OutputFormatTy::md, "md",
113671bac74SJulie Hockett                                            "Documentation in MD format."),
114671bac74SJulie Hockett                                 clEnumValN(OutputFormatTy::html, "html",
115671bac74SJulie Hockett                                            "Documentation in HTML format.")),
116ac68cab9SJulie Hockett                llvm::cl::init(OutputFormatTy::yaml),
117ac68cab9SJulie Hockett                llvm::cl::cat(ClangDocCategory));
118d0f9a872SJulie Hockett 
119229c63b0SJulie Hockett std::string getFormatString() {
120229c63b0SJulie Hockett   switch (FormatEnum) {
121229c63b0SJulie Hockett   case OutputFormatTy::yaml:
122229c63b0SJulie Hockett     return "yaml";
123229c63b0SJulie Hockett   case OutputFormatTy::md:
124229c63b0SJulie Hockett     return "md";
125671bac74SJulie Hockett   case OutputFormatTy::html:
126671bac74SJulie Hockett     return "html";
127229c63b0SJulie Hockett   }
128229c63b0SJulie Hockett   llvm_unreachable("Unknown OutputFormatTy");
129229c63b0SJulie Hockett }
130e975a473SJulie Hockett 
131db5d8e3dSDiego Astiazaran // This function isn't referenced outside its translation unit, but it
132db5d8e3dSDiego Astiazaran // can't use the "static" keyword because its address is used for
133db5d8e3dSDiego Astiazaran // GetMainExecutable (since some platforms don't support taking the
134db5d8e3dSDiego Astiazaran // address of main, and some platforms can't implement GetMainExecutable
135db5d8e3dSDiego Astiazaran // without being given the address of a function in the main executable).
136f14ad744SPeterChou1 std::string getExecutablePath(const char *Argv0, void *MainAddr) {
137db5d8e3dSDiego Astiazaran   return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
138db5d8e3dSDiego Astiazaran }
139db5d8e3dSDiego Astiazaran 
140f14ad744SPeterChou1 llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
141f14ad744SPeterChou1   using DirIt = llvm::sys::fs::directory_iterator;
142f14ad744SPeterChou1   std::error_code FileErr;
143f14ad744SPeterChou1   llvm::SmallString<128> FilePath(UserAssetPath);
144f14ad744SPeterChou1   for (DirIt DirStart = DirIt(UserAssetPath, FileErr),
145f14ad744SPeterChou1                    DirEnd;
146f14ad744SPeterChou1        !FileErr && DirStart != DirEnd; DirStart.increment(FileErr)) {
147f14ad744SPeterChou1     FilePath = DirStart->path();
148f14ad744SPeterChou1     if (llvm::sys::fs::is_regular_file(FilePath)) {
149f14ad744SPeterChou1       if (llvm::sys::path::extension(FilePath) == ".css")
150f14ad744SPeterChou1         CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
151f14ad744SPeterChou1                                      std::string(FilePath));
152f14ad744SPeterChou1       else if (llvm::sys::path::extension(FilePath) == ".js")
153f14ad744SPeterChou1         CDCtx.JsScripts.emplace_back(FilePath.str());
154f14ad744SPeterChou1     }
155f14ad744SPeterChou1   }
156f14ad744SPeterChou1   if (FileErr)
157f14ad744SPeterChou1     return llvm::createFileError(FilePath, FileErr);
158f14ad744SPeterChou1   return llvm::Error::success();
159f14ad744SPeterChou1 }
160f14ad744SPeterChou1 
161f14ad744SPeterChou1 llvm::Error getDefaultAssetFiles(const char *Argv0,
162f14ad744SPeterChou1                                  clang::doc::ClangDocContext &CDCtx) {
163f14ad744SPeterChou1   void *MainAddr = (void *)(intptr_t)getExecutablePath;
164f14ad744SPeterChou1   std::string ClangDocPath = getExecutablePath(Argv0, MainAddr);
165f14ad744SPeterChou1   llvm::SmallString<128> NativeClangDocPath;
166f14ad744SPeterChou1   llvm::sys::path::native(ClangDocPath, NativeClangDocPath);
167f14ad744SPeterChou1 
168f14ad744SPeterChou1   llvm::SmallString<128> AssetsPath;
169f14ad744SPeterChou1   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
170f14ad744SPeterChou1   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
171f14ad744SPeterChou1   llvm::SmallString<128> DefaultStylesheet;
172f14ad744SPeterChou1   llvm::sys::path::native(AssetsPath, DefaultStylesheet);
173f14ad744SPeterChou1   llvm::sys::path::append(DefaultStylesheet,
174f14ad744SPeterChou1                           "clang-doc-default-stylesheet.css");
175f14ad744SPeterChou1   llvm::SmallString<128> IndexJS;
176f14ad744SPeterChou1   llvm::sys::path::native(AssetsPath, IndexJS);
177f14ad744SPeterChou1   llvm::sys::path::append(IndexJS, "index.js");
178f14ad744SPeterChou1 
179f14ad744SPeterChou1   if (!llvm::sys::fs::is_regular_file(IndexJS))
180f14ad744SPeterChou1     return llvm::createStringError(llvm::inconvertibleErrorCode(),
181f14ad744SPeterChou1                                    "default index.js file missing at " +
182f14ad744SPeterChou1                                        IndexJS + "\n");
183f14ad744SPeterChou1 
184f14ad744SPeterChou1   if (!llvm::sys::fs::is_regular_file(DefaultStylesheet))
185f14ad744SPeterChou1     return llvm::createStringError(
186f14ad744SPeterChou1         llvm::inconvertibleErrorCode(),
187f14ad744SPeterChou1         "default clang-doc-default-stylesheet.css file missing at " +
188f14ad744SPeterChou1             DefaultStylesheet + "\n");
189f14ad744SPeterChou1 
190f14ad744SPeterChou1   CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
191f14ad744SPeterChou1                                std::string(DefaultStylesheet));
192f14ad744SPeterChou1   CDCtx.JsScripts.emplace_back(IndexJS.str());
193f14ad744SPeterChou1 
194f14ad744SPeterChou1   return llvm::Error::success();
195f14ad744SPeterChou1 }
196f14ad744SPeterChou1 
197f14ad744SPeterChou1 llvm::Error getHtmlAssetFiles(const char *Argv0,
198f14ad744SPeterChou1                               clang::doc::ClangDocContext &CDCtx) {
199f14ad744SPeterChou1   if (!UserAssetPath.empty() &&
200f14ad744SPeterChou1       !llvm::sys::fs::is_directory(std::string(UserAssetPath)))
201f14ad744SPeterChou1     llvm::outs() << "Asset path supply is not a directory: " << UserAssetPath
202f14ad744SPeterChou1                  << " falling back to default\n";
203f14ad744SPeterChou1   if (llvm::sys::fs::is_directory(std::string(UserAssetPath)))
204f14ad744SPeterChou1     return getAssetFiles(CDCtx);
205f14ad744SPeterChou1   return getDefaultAssetFiles(Argv0, CDCtx);
206f14ad744SPeterChou1 }
207f14ad744SPeterChou1 
2083c9e3457SPeterChou1 /// Make the output of clang-doc deterministic by sorting the children of
2093c9e3457SPeterChou1 /// namespaces and records.
2103c9e3457SPeterChou1 void sortUsrToInfo(llvm::StringMap<std::unique_ptr<doc::Info>> &USRToInfo) {
2113c9e3457SPeterChou1   for (auto &I : USRToInfo) {
2123c9e3457SPeterChou1     auto &Info = I.second;
2133c9e3457SPeterChou1     if (Info->IT == doc::InfoType::IT_namespace) {
2143c9e3457SPeterChou1       auto *Namespace = static_cast<clang::doc::NamespaceInfo *>(Info.get());
2153c9e3457SPeterChou1       Namespace->Children.sort();
2163c9e3457SPeterChou1     }
2173c9e3457SPeterChou1     if (Info->IT == doc::InfoType::IT_record) {
2183c9e3457SPeterChou1       auto *Record = static_cast<clang::doc::RecordInfo *>(Info.get());
2193c9e3457SPeterChou1       Record->Children.sort();
2203c9e3457SPeterChou1     }
2213c9e3457SPeterChou1   }
2223c9e3457SPeterChou1 }
2233c9e3457SPeterChou1 
224e975a473SJulie Hockett int main(int argc, const char **argv) {
2256f773205SNico Weber   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
226e78f3018SJulie Hockett   std::error_code OK;
227e78f3018SJulie Hockett 
228ea50901aSPetr Hosek   const char *Overview =
229ea50901aSPetr Hosek       R"(Generates documentation from source code and comments.
230e975a473SJulie Hockett 
231ea50901aSPetr Hosek Example usage for files without flags (default):
232ea50901aSPetr Hosek 
233ea50901aSPetr Hosek   $ clang-doc File1.cpp File2.cpp ... FileN.cpp
234ea50901aSPetr Hosek 
235ea50901aSPetr Hosek Example usage for a project using a compile commands database:
236ea50901aSPetr Hosek 
237ea50901aSPetr Hosek   $ clang-doc --executor=all-TUs compile_commands.json
238ea50901aSPetr Hosek )";
239ea50901aSPetr Hosek 
240ea50901aSPetr Hosek   auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
241ea50901aSPetr Hosek       argc, argv, ClangDocCategory, Overview);
242ea50901aSPetr Hosek 
243ea50901aSPetr Hosek   if (!Executor) {
244ea50901aSPetr Hosek     llvm::errs() << toString(Executor.takeError()) << "\n";
245e975a473SJulie Hockett     return 1;
246e975a473SJulie Hockett   }
247e975a473SJulie Hockett 
248ac68cab9SJulie Hockett   // Fail early if an invalid format was provided.
249ac68cab9SJulie Hockett   std::string Format = getFormatString();
250ac68cab9SJulie Hockett   llvm::outs() << "Emiting docs in " << Format << " format.\n";
251ac68cab9SJulie Hockett   auto G = doc::findGeneratorByName(Format);
252ac68cab9SJulie Hockett   if (!G) {
253ac68cab9SJulie Hockett     llvm::errs() << toString(G.takeError()) << "\n";
254ac68cab9SJulie Hockett     return 1;
255ac68cab9SJulie Hockett   }
256ac68cab9SJulie Hockett 
257e975a473SJulie Hockett   ArgumentsAdjuster ArgAdjuster;
258e975a473SJulie Hockett   if (!DoxygenOnly)
259e975a473SJulie Hockett     ArgAdjuster = combineAdjusters(
260e975a473SJulie Hockett         getInsertArgumentAdjuster("-fparse-all-comments",
261e975a473SJulie Hockett                                   tooling::ArgumentInsertPosition::END),
262e975a473SJulie Hockett         ArgAdjuster);
263e975a473SJulie Hockett 
264acd35f6cSDiego Astiazaran   clang::doc::ClangDocContext CDCtx = {
265ea50901aSPetr Hosek       Executor->get()->getExecutionContext(),
2663550da79SDiego Astiazaran       ProjectName,
267acd35f6cSDiego Astiazaran       PublicOnly,
268acd35f6cSDiego Astiazaran       OutDirectory,
26977dc05b9SDiego Astiazaran       SourceRoot,
270665e9676SDiego Astiazaran       RepositoryUrl,
27180d1c6acSPeterChou1       {UserStylesheets.begin(), UserStylesheets.end()}
27280d1c6acSPeterChou1   };
273acd35f6cSDiego Astiazaran 
274acd35f6cSDiego Astiazaran   if (Format == "html") {
275f14ad744SPeterChou1     if (auto Err = getHtmlAssetFiles(argv[0], CDCtx)) {
276f14ad744SPeterChou1       llvm::errs() << toString(std::move(Err)) << "\n";
277f14ad744SPeterChou1       return 1;
278f14ad744SPeterChou1     }
279acd35f6cSDiego Astiazaran   }
280acd35f6cSDiego Astiazaran 
281acd35f6cSDiego Astiazaran   // Mapping phase
282acd35f6cSDiego Astiazaran   llvm::outs() << "Mapping decls...\n";
283eb50a2e8SJulie Hockett   auto Err =
284ea50901aSPetr Hosek       Executor->get()->execute(doc::newMapperActionFactory(CDCtx), ArgAdjuster);
285d0f9a872SJulie Hockett   if (Err) {
2868fb7074dSDiego Astiazaran     if (IgnoreMappingFailures)
2878fb7074dSDiego Astiazaran       llvm::errs() << "Error mapping decls in files. Clang-doc will ignore "
2888fb7074dSDiego Astiazaran                       "these files and continue:\n"
2898fb7074dSDiego Astiazaran                    << toString(std::move(Err)) << "\n";
2908fb7074dSDiego Astiazaran     else {
291e975a473SJulie Hockett       llvm::errs() << toString(std::move(Err)) << "\n";
292d0f9a872SJulie Hockett       return 1;
293d0f9a872SJulie Hockett     }
2948fb7074dSDiego Astiazaran   }
295e975a473SJulie Hockett 
296d0f9a872SJulie Hockett   // Collect values into output by key.
297d0f9a872SJulie Hockett   // In ToolResults, the Key is the hashed USR and the value is the
298d0f9a872SJulie Hockett   // bitcode-encoded representation of the Info object.
2998899c29bSJulie Hockett   llvm::outs() << "Collecting infos...\n";
3006ab28e8cSDiego Astiazaran   llvm::StringMap<std::vector<StringRef>> USRToBitcode;
301ea50901aSPetr Hosek   Executor->get()->getToolResults()->forEachResult(
3026ab28e8cSDiego Astiazaran       [&](StringRef Key, StringRef Value) {
303*b43cfa7eSKazu Hirata         USRToBitcode[Key].emplace_back(Value);
3046ab28e8cSDiego Astiazaran       });
305a9cb2dd8SJulie Hockett 
3067b8c7e02SBrett Wilson   // Collects all Infos according to their unique USR value. This map is added
3077b8c7e02SBrett Wilson   // to from the thread pool below and is protected by the USRToInfoMutex.
3087b8c7e02SBrett Wilson   llvm::sys::Mutex USRToInfoMutex;
3097b8c7e02SBrett Wilson   llvm::StringMap<std::unique_ptr<doc::Info>> USRToInfo;
3107b8c7e02SBrett Wilson 
3118899c29bSJulie Hockett   // First reducing phase (reduce all decls into one info per decl).
3126ab28e8cSDiego Astiazaran   llvm::outs() << "Reducing " << USRToBitcode.size() << " infos...\n";
3136ab28e8cSDiego Astiazaran   std::atomic<bool> Error;
3146ab28e8cSDiego Astiazaran   Error = false;
315e2d45770SDiego Astiazaran   llvm::sys::Mutex IndexMutex;
3166ab28e8cSDiego Astiazaran   // ExecutorConcurrency is a flag exposed by AllTUsExecution.h
317716042a6SMehdi Amini   llvm::DefaultThreadPool Pool(llvm::hardware_concurrency(ExecutorConcurrency));
3186ab28e8cSDiego Astiazaran   for (auto &Group : USRToBitcode) {
3196ab28e8cSDiego Astiazaran     Pool.async([&]() {
3207868c04dSPeterChou1       std::vector<std::unique_ptr<doc::Info>> Infos;
3216ab28e8cSDiego Astiazaran       for (auto &Bitcode : Group.getValue()) {
3226ab28e8cSDiego Astiazaran         llvm::BitstreamCursor Stream(Bitcode);
3236ab28e8cSDiego Astiazaran         doc::ClangDocBitcodeReader Reader(Stream);
3246ab28e8cSDiego Astiazaran         auto ReadInfos = Reader.readBitcode();
3256ab28e8cSDiego Astiazaran         if (!ReadInfos) {
3266ab28e8cSDiego Astiazaran           llvm::errs() << toString(ReadInfos.takeError()) << "\n";
3276ab28e8cSDiego Astiazaran           Error = true;
3286ab28e8cSDiego Astiazaran           return;
3296ab28e8cSDiego Astiazaran         }
3306ab28e8cSDiego Astiazaran         std::move(ReadInfos->begin(), ReadInfos->end(),
3316ab28e8cSDiego Astiazaran                   std::back_inserter(Infos));
3326ab28e8cSDiego Astiazaran       }
3336ab28e8cSDiego Astiazaran 
3346ab28e8cSDiego Astiazaran       auto Reduced = doc::mergeInfos(Infos);
3358899c29bSJulie Hockett       if (!Reduced) {
336a9cb2dd8SJulie Hockett         llvm::errs() << llvm::toString(Reduced.takeError());
3376ab28e8cSDiego Astiazaran         return;
3388899c29bSJulie Hockett       }
339d0f9a872SJulie Hockett 
34048bb1471SPaul Kirth       // Add a reference to this Info in the Index
3417b8c7e02SBrett Wilson       {
342c9951209SPetr Hosek         std::lock_guard<llvm::sys::Mutex> Guard(IndexMutex);
3437b8c7e02SBrett Wilson         clang::doc::Generator::addInfoToIndex(CDCtx.Idx, Reduced.get().get());
3447b8c7e02SBrett Wilson       }
3457868c04dSPeterChou1 
34680d1c6acSPeterChou1       // Save in the result map (needs a lock due to threaded access).
3477b8c7e02SBrett Wilson       {
348c9951209SPetr Hosek         std::lock_guard<llvm::sys::Mutex> Guard(USRToInfoMutex);
3497b8c7e02SBrett Wilson         USRToInfo[Group.getKey()] = std::move(Reduced.get());
3507b8c7e02SBrett Wilson       }
3516ab28e8cSDiego Astiazaran     });
352e975a473SJulie Hockett   }
353e975a473SJulie Hockett 
3546ab28e8cSDiego Astiazaran   Pool.wait();
3556ab28e8cSDiego Astiazaran 
3566ab28e8cSDiego Astiazaran   if (Error)
3576ab28e8cSDiego Astiazaran     return 1;
3586ab28e8cSDiego Astiazaran 
3593c9e3457SPeterChou1   sortUsrToInfo(USRToInfo);
3603c9e3457SPeterChou1 
3617b8c7e02SBrett Wilson   // Ensure the root output directory exists.
3627b8c7e02SBrett Wilson   if (std::error_code Err = llvm::sys::fs::create_directories(OutDirectory);
3637b8c7e02SBrett Wilson       Err != std::error_code()) {
3647b8c7e02SBrett Wilson     llvm::errs() << "Failed to create directory '" << OutDirectory << "'\n";
3657b8c7e02SBrett Wilson     return 1;
3667b8c7e02SBrett Wilson   }
3677b8c7e02SBrett Wilson 
3687b8c7e02SBrett Wilson   // Run the generator.
3697b8c7e02SBrett Wilson   llvm::outs() << "Generating docs...\n";
3707b8c7e02SBrett Wilson   if (auto Err =
3717b8c7e02SBrett Wilson           G->get()->generateDocs(OutDirectory, std::move(USRToInfo), CDCtx)) {
3727b8c7e02SBrett Wilson     llvm::errs() << toString(std::move(Err)) << "\n";
3737b8c7e02SBrett Wilson     return 1;
3747b8c7e02SBrett Wilson   }
3757b8c7e02SBrett Wilson 
3767dfe0bc3SDiego Astiazaran   llvm::outs() << "Generating assets for docs...\n";
37772e1f7f9SJulie Hockett   Err = G->get()->createResources(CDCtx);
37872e1f7f9SJulie Hockett   if (Err) {
37970ec8419SPeterChou1     llvm::outs() << "warning: " << toString(std::move(Err)) << "\n";
38072e1f7f9SJulie Hockett   }
381db5d8e3dSDiego Astiazaran 
382e975a473SJulie Hockett   return 0;
383e975a473SJulie Hockett }
384