[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / clang-tools-extra / clang-doc / tool / ClangDocMain.cpp
blob2ece3e4c071dc019f2b680917aec384a88715055
1 //===-- ClangDocMain.cpp - ClangDoc -----------------------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This tool for generating C and C++ documentation from source code
10 // and comments. Generally, it runs a LibTooling FrontendAction on source files,
11 // mapping each declaration in those files to its USR and serializing relevant
12 // information into LLVM bitcode. It then runs a pass over the collected
13 // declaration information, reducing by USR. There is an option to dump this
14 // intermediate result to bitcode. Finally, it hands the reduced information
15 // off to a generator, which does the final parsing from the intermediate
16 // representation to the desired output format.
18 //===----------------------------------------------------------------------===//
20 #include "BitcodeReader.h"
21 #include "BitcodeWriter.h"
22 #include "ClangDoc.h"
23 #include "Generators.h"
24 #include "Representation.h"
25 #include "clang/AST/AST.h"
26 #include "clang/AST/Decl.h"
27 #include "clang/ASTMatchers/ASTMatchFinder.h"
28 #include "clang/ASTMatchers/ASTMatchersInternal.h"
29 #include "clang/Driver/Options.h"
30 #include "clang/Frontend/FrontendActions.h"
31 #include "clang/Tooling/AllTUsExecution.h"
32 #include "clang/Tooling/CommonOptionsParser.h"
33 #include "clang/Tooling/Execution.h"
34 #include "clang/Tooling/Tooling.h"
35 #include "llvm/ADT/APFloat.h"
36 #include "llvm/Support/CommandLine.h"
37 #include "llvm/Support/Error.h"
38 #include "llvm/Support/FileSystem.h"
39 #include "llvm/Support/Mutex.h"
40 #include "llvm/Support/Path.h"
41 #include "llvm/Support/Process.h"
42 #include "llvm/Support/Signals.h"
43 #include "llvm/Support/ThreadPool.h"
44 #include "llvm/Support/raw_ostream.h"
45 #include <atomic>
46 #include <mutex>
47 #include <string>
49 using namespace clang::ast_matchers;
50 using namespace clang::tooling;
51 using namespace clang;
53 static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
54 static llvm::cl::OptionCategory ClangDocCategory("clang-doc options");
56 static llvm::cl::opt<std::string>
57 ProjectName("project-name", llvm::cl::desc("Name of project."),
58 llvm::cl::cat(ClangDocCategory));
60 static llvm::cl::opt<bool> IgnoreMappingFailures(
61 "ignore-map-errors",
62 llvm::cl::desc("Continue if files are not mapped correctly."),
63 llvm::cl::init(true), llvm::cl::cat(ClangDocCategory));
65 static llvm::cl::opt<std::string>
66 OutDirectory("output",
67 llvm::cl::desc("Directory for outputting generated files."),
68 llvm::cl::init("docs"), llvm::cl::cat(ClangDocCategory));
70 static llvm::cl::opt<bool>
71 PublicOnly("public", llvm::cl::desc("Document only public declarations."),
72 llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
74 static llvm::cl::opt<bool> DoxygenOnly(
75 "doxygen",
76 llvm::cl::desc("Use only doxygen-style comments to generate docs."),
77 llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
79 static llvm::cl::list<std::string> UserStylesheets(
80 "stylesheets", llvm::cl::CommaSeparated,
81 llvm::cl::desc("CSS stylesheets to extend the default styles."),
82 llvm::cl::cat(ClangDocCategory));
84 static llvm::cl::opt<std::string> SourceRoot("source-root", llvm::cl::desc(R"(
85 Directory where processed files are stored.
86 Links to definition locations will only be
87 generated if the file is in this dir.)"),
88 llvm::cl::cat(ClangDocCategory));
90 static llvm::cl::opt<std::string>
91 RepositoryUrl("repository", llvm::cl::desc(R"(
92 URL of repository that hosts code.
93 Used for links to definition locations.)"),
94 llvm::cl::cat(ClangDocCategory));
96 enum OutputFormatTy {
97 md,
98 yaml,
99 html,
102 static llvm::cl::opt<OutputFormatTy>
103 FormatEnum("format", llvm::cl::desc("Format for outputted docs."),
104 llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml",
105 "Documentation in YAML format."),
106 clEnumValN(OutputFormatTy::md, "md",
107 "Documentation in MD format."),
108 clEnumValN(OutputFormatTy::html, "html",
109 "Documentation in HTML format.")),
110 llvm::cl::init(OutputFormatTy::yaml),
111 llvm::cl::cat(ClangDocCategory));
113 std::string getFormatString() {
114 switch (FormatEnum) {
115 case OutputFormatTy::yaml:
116 return "yaml";
117 case OutputFormatTy::md:
118 return "md";
119 case OutputFormatTy::html:
120 return "html";
122 llvm_unreachable("Unknown OutputFormatTy");
125 // This function isn't referenced outside its translation unit, but it
126 // can't use the "static" keyword because its address is used for
127 // GetMainExecutable (since some platforms don't support taking the
128 // address of main, and some platforms can't implement GetMainExecutable
129 // without being given the address of a function in the main executable).
130 std::string GetExecutablePath(const char *Argv0, void *MainAddr) {
131 return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
134 int main(int argc, const char **argv) {
135 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
136 std::error_code OK;
138 const char *Overview =
139 R"(Generates documentation from source code and comments.
141 Example usage for files without flags (default):
143 $ clang-doc File1.cpp File2.cpp ... FileN.cpp
145 Example usage for a project using a compile commands database:
147 $ clang-doc --executor=all-TUs compile_commands.json
150 auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
151 argc, argv, ClangDocCategory, Overview);
153 if (!Executor) {
154 llvm::errs() << toString(Executor.takeError()) << "\n";
155 return 1;
158 // Fail early if an invalid format was provided.
159 std::string Format = getFormatString();
160 llvm::outs() << "Emiting docs in " << Format << " format.\n";
161 auto G = doc::findGeneratorByName(Format);
162 if (!G) {
163 llvm::errs() << toString(G.takeError()) << "\n";
164 return 1;
167 ArgumentsAdjuster ArgAdjuster;
168 if (!DoxygenOnly)
169 ArgAdjuster = combineAdjusters(
170 getInsertArgumentAdjuster("-fparse-all-comments",
171 tooling::ArgumentInsertPosition::END),
172 ArgAdjuster);
174 clang::doc::ClangDocContext CDCtx = {
175 Executor->get()->getExecutionContext(),
176 ProjectName,
177 PublicOnly,
178 OutDirectory,
179 SourceRoot,
180 RepositoryUrl,
181 {UserStylesheets.begin(), UserStylesheets.end()},
182 {"index.js", "index_json.js"}};
184 if (Format == "html") {
185 void *MainAddr = (void *)(intptr_t)GetExecutablePath;
186 std::string ClangDocPath = GetExecutablePath(argv[0], MainAddr);
187 llvm::SmallString<128> NativeClangDocPath;
188 llvm::sys::path::native(ClangDocPath, NativeClangDocPath);
189 llvm::SmallString<128> AssetsPath;
190 AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
191 llvm::sys::path::append(AssetsPath, "..", "share", "clang");
192 llvm::SmallString<128> DefaultStylesheet;
193 llvm::sys::path::native(AssetsPath, DefaultStylesheet);
194 llvm::sys::path::append(DefaultStylesheet,
195 "clang-doc-default-stylesheet.css");
196 llvm::SmallString<128> IndexJS;
197 llvm::sys::path::native(AssetsPath, IndexJS);
198 llvm::sys::path::append(IndexJS, "index.js");
199 CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
200 std::string(DefaultStylesheet.str()));
201 CDCtx.FilesToCopy.emplace_back(IndexJS.str());
204 // Mapping phase
205 llvm::outs() << "Mapping decls...\n";
206 auto Err =
207 Executor->get()->execute(doc::newMapperActionFactory(CDCtx), ArgAdjuster);
208 if (Err) {
209 if (IgnoreMappingFailures)
210 llvm::errs() << "Error mapping decls in files. Clang-doc will ignore "
211 "these files and continue:\n"
212 << toString(std::move(Err)) << "\n";
213 else {
214 llvm::errs() << toString(std::move(Err)) << "\n";
215 return 1;
219 // Collect values into output by key.
220 // In ToolResults, the Key is the hashed USR and the value is the
221 // bitcode-encoded representation of the Info object.
222 llvm::outs() << "Collecting infos...\n";
223 llvm::StringMap<std::vector<StringRef>> USRToBitcode;
224 Executor->get()->getToolResults()->forEachResult(
225 [&](StringRef Key, StringRef Value) {
226 auto R = USRToBitcode.try_emplace(Key, std::vector<StringRef>());
227 R.first->second.emplace_back(Value);
230 // Collects all Infos according to their unique USR value. This map is added
231 // to from the thread pool below and is protected by the USRToInfoMutex.
232 llvm::sys::Mutex USRToInfoMutex;
233 llvm::StringMap<std::unique_ptr<doc::Info>> USRToInfo;
235 // First reducing phase (reduce all decls into one info per decl).
236 llvm::outs() << "Reducing " << USRToBitcode.size() << " infos...\n";
237 std::atomic<bool> Error;
238 Error = false;
239 llvm::sys::Mutex IndexMutex;
240 // ExecutorConcurrency is a flag exposed by AllTUsExecution.h
241 llvm::ThreadPool Pool(llvm::hardware_concurrency(ExecutorConcurrency));
242 for (auto &Group : USRToBitcode) {
243 Pool.async([&]() {
244 std::vector<std::unique_ptr<doc::Info>> Infos;
246 for (auto &Bitcode : Group.getValue()) {
247 llvm::BitstreamCursor Stream(Bitcode);
248 doc::ClangDocBitcodeReader Reader(Stream);
249 auto ReadInfos = Reader.readBitcode();
250 if (!ReadInfos) {
251 llvm::errs() << toString(ReadInfos.takeError()) << "\n";
252 Error = true;
253 return;
255 std::move(ReadInfos->begin(), ReadInfos->end(),
256 std::back_inserter(Infos));
259 auto Reduced = doc::mergeInfos(Infos);
260 if (!Reduced) {
261 llvm::errs() << llvm::toString(Reduced.takeError());
262 return;
265 // Add a reference to this Info in the Index
267 std::lock_guard<llvm::sys::Mutex> Guard(IndexMutex);
268 clang::doc::Generator::addInfoToIndex(CDCtx.Idx, Reduced.get().get());
271 // Save in the result map (needs a lock due to threaded access).
273 std::lock_guard<llvm::sys::Mutex> Guard(USRToInfoMutex);
274 USRToInfo[Group.getKey()] = std::move(Reduced.get());
279 Pool.wait();
281 if (Error)
282 return 1;
284 // Ensure the root output directory exists.
285 if (std::error_code Err = llvm::sys::fs::create_directories(OutDirectory);
286 Err != std::error_code()) {
287 llvm::errs() << "Failed to create directory '" << OutDirectory << "'\n";
288 return 1;
291 // Run the generator.
292 llvm::outs() << "Generating docs...\n";
293 if (auto Err =
294 G->get()->generateDocs(OutDirectory, std::move(USRToInfo), CDCtx)) {
295 llvm::errs() << toString(std::move(Err)) << "\n";
296 return 1;
299 llvm::outs() << "Generating assets for docs...\n";
300 Err = G->get()->createResources(CDCtx);
301 if (Err) {
302 llvm::errs() << toString(std::move(Err)) << "\n";
303 return 1;
306 return 0;