1 //===-- ClangDocMain.cpp - ClangDoc -----------------------------*- 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 // 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"
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"
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(
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(
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
));
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
:
117 case OutputFormatTy::md
:
119 case OutputFormatTy::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]);
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
);
154 llvm::errs() << toString(Executor
.takeError()) << "\n";
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
);
163 llvm::errs() << toString(G
.takeError()) << "\n";
167 ArgumentsAdjuster ArgAdjuster
;
169 ArgAdjuster
= combineAdjusters(
170 getInsertArgumentAdjuster("-fparse-all-comments",
171 tooling::ArgumentInsertPosition::END
),
174 clang::doc::ClangDocContext CDCtx
= {
175 Executor
->get()->getExecutionContext(),
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());
205 llvm::outs() << "Mapping decls...\n";
207 Executor
->get()->execute(doc::newMapperActionFactory(CDCtx
), ArgAdjuster
);
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";
214 llvm::errs() << toString(std::move(Err
)) << "\n";
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
;
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
) {
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();
251 llvm::errs() << toString(ReadInfos
.takeError()) << "\n";
255 std::move(ReadInfos
->begin(), ReadInfos
->end(),
256 std::back_inserter(Infos
));
259 auto Reduced
= doc::mergeInfos(Infos
);
261 llvm::errs() << llvm::toString(Reduced
.takeError());
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());
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";
291 // Run the generator.
292 llvm::outs() << "Generating docs...\n";
294 G
->get()->generateDocs(OutDirectory
, std::move(USRToInfo
), CDCtx
)) {
295 llvm::errs() << toString(std::move(Err
)) << "\n";
299 llvm::outs() << "Generating assets for docs...\n";
300 Err
= G
->get()->createResources(CDCtx
);
302 llvm::errs() << toString(std::move(Err
)) << "\n";