blob: 2b44869d406b45d1d021b81958577ba9e6f8657a [file] [log] [blame]
Julie Hockette975a472018-03-22 23:34:46 +00001//===-- ClangDocMain.cpp - ClangDoc -----------------------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Julie Hockette975a472018-03-22 23:34:46 +00006//
7//===----------------------------------------------------------------------===//
8//
9// This tool for generating C and C++ documenation 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.
17//
18//===----------------------------------------------------------------------===//
19
Julie Hockettd0f9a872018-06-04 17:22:20 +000020#include "BitcodeReader.h"
21#include "BitcodeWriter.h"
Julie Hockette975a472018-03-22 23:34:46 +000022#include "ClangDoc.h"
Julie Hockette78f3012018-06-06 16:13:17 +000023#include "Generators.h"
Julie Hockettd0f9a872018-06-04 17:22:20 +000024#include "Representation.h"
Julie Hockette975a472018-03-22 23:34:46 +000025#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/CommonOptionsParser.h"
32#include "clang/Tooling/Execution.h"
Julie Hockette975a472018-03-22 23:34:46 +000033#include "clang/Tooling/Tooling.h"
34#include "llvm/ADT/APFloat.h"
Julie Hockettac68cab2018-09-11 15:56:55 +000035#include "llvm/Support/CommandLine.h"
Julie Hockettd0f9a872018-06-04 17:22:20 +000036#include "llvm/Support/Error.h"
Julie Hockette975a472018-03-22 23:34:46 +000037#include "llvm/Support/FileSystem.h"
38#include "llvm/Support/Path.h"
39#include "llvm/Support/Process.h"
40#include "llvm/Support/Signals.h"
41#include "llvm/Support/raw_ostream.h"
42#include <string>
43
44using namespace clang::ast_matchers;
45using namespace clang::tooling;
46using namespace clang;
47
48static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
49static llvm::cl::OptionCategory ClangDocCategory("clang-doc options");
50
51static llvm::cl::opt<std::string>
52 OutDirectory("output",
53 llvm::cl::desc("Directory for outputting generated files."),
54 llvm::cl::init("docs"), llvm::cl::cat(ClangDocCategory));
55
56static llvm::cl::opt<bool>
Julie Hocketteb50a2e2018-07-20 18:49:55 +000057 PublicOnly("public", llvm::cl::desc("Document only public declarations."),
58 llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
59
Julie Hockett229c63b2018-10-16 23:07:37 +000060static llvm::cl::opt<bool> DoxygenOnly(
61 "doxygen",
62 llvm::cl::desc("Use only doxygen-style comments to generate docs."),
63 llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
64
Julie Hockettd0f9a872018-06-04 17:22:20 +000065enum OutputFormatTy {
Julie Hockettac68cab2018-09-11 15:56:55 +000066 md,
Julie Hockettd0f9a872018-06-04 17:22:20 +000067 yaml,
68};
69
Julie Hockettac68cab2018-09-11 15:56:55 +000070static llvm::cl::opt<OutputFormatTy>
71 FormatEnum("format", llvm::cl::desc("Format for outputted docs."),
72 llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml",
73 "Documentation in YAML format."),
74 clEnumValN(OutputFormatTy::md, "md",
75 "Documentation in MD format.")),
76 llvm::cl::init(OutputFormatTy::yaml),
77 llvm::cl::cat(ClangDocCategory));
Julie Hockettd0f9a872018-06-04 17:22:20 +000078
Julie Hockett229c63b2018-10-16 23:07:37 +000079std::string getFormatString() {
80 switch (FormatEnum) {
81 case OutputFormatTy::yaml:
82 return "yaml";
83 case OutputFormatTy::md:
84 return "md";
85 }
86 llvm_unreachable("Unknown OutputFormatTy");
87}
Julie Hockette975a472018-03-22 23:34:46 +000088
Julie Hockettd0f9a872018-06-04 17:22:20 +000089bool CreateDirectory(const Twine &DirName, bool ClearDirectory = false) {
90 std::error_code OK;
91 llvm::SmallString<128> DocsRootPath;
92 if (ClearDirectory) {
93 std::error_code RemoveStatus = llvm::sys::fs::remove_directories(DirName);
94 if (RemoveStatus != OK) {
95 llvm::errs() << "Unable to remove existing documentation directory for "
96 << DirName << ".\n";
97 return true;
98 }
99 }
100 std::error_code DirectoryStatus = llvm::sys::fs::create_directories(DirName);
101 if (DirectoryStatus != OK) {
102 llvm::errs() << "Unable to create documentation directories.\n";
103 return true;
104 }
105 return false;
106}
107
Julie Hockett8899c292018-08-02 20:10:17 +0000108// A function to extract the appropriate path name for a given info's
109// documentation. The path returned is a composite of the parent namespaces as
110// directories plus the decl name as the filename.
111//
112// Example: Given the below, the <ext> path for class C will be <
113// root>/A/B/C.<ext>
114//
115// namespace A {
116// namesapce B {
117//
118// class C {};
119//
120// }
121// }
Julie Hockette78f3012018-06-06 16:13:17 +0000122llvm::Expected<llvm::SmallString<128>>
Julie Hockett8899c292018-08-02 20:10:17 +0000123getInfoOutputFile(StringRef Root,
124 llvm::SmallVectorImpl<doc::Reference> &Namespaces,
125 StringRef Name, StringRef Ext) {
Julie Hockette78f3012018-06-06 16:13:17 +0000126 std::error_code OK;
127 llvm::SmallString<128> Path;
128 llvm::sys::path::native(Root, Path);
129 for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
130 llvm::sys::path::append(Path, R->Name);
131
132 if (CreateDirectory(Path))
133 return llvm::make_error<llvm::StringError>("Unable to create directory.\n",
134 llvm::inconvertibleErrorCode());
135
Julie Hockett8899c292018-08-02 20:10:17 +0000136 if (Name.empty())
137 Name = "GlobalNamespace";
Julie Hockette78f3012018-06-06 16:13:17 +0000138 llvm::sys::path::append(Path, Name + Ext);
139 return Path;
140}
141
Julie Hockett8899c292018-08-02 20:10:17 +0000142// Iterate through tool results and build string map of info vectors from the
143// encoded bitstreams.
144bool bitcodeResultsToInfos(
145 tooling::ToolResults &Results,
146 llvm::StringMap<std::vector<std::unique_ptr<doc::Info>>> &Output) {
147 bool Err = false;
148 Results.forEachResult([&](StringRef Key, StringRef Value) {
149 llvm::BitstreamCursor Stream(Value);
150 doc::ClangDocBitcodeReader Reader(Stream);
151 auto Infos = Reader.readBitcode();
152 if (!Infos) {
153 llvm::errs() << toString(Infos.takeError()) << "\n";
154 Err = true;
155 return;
156 }
157 for (auto &I : Infos.get()) {
158 auto R =
159 Output.try_emplace(Key, std::vector<std::unique_ptr<doc::Info>>());
160 R.first->second.emplace_back(std::move(I));
161 }
162 });
163 return Err;
164}
165
Julie Hockette975a472018-03-22 23:34:46 +0000166int main(int argc, const char **argv) {
167 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
Julie Hockette78f3012018-06-06 16:13:17 +0000168 std::error_code OK;
169
Julie Hockett9e22b4c2018-10-26 19:11:34 +0000170 ExecutorName.setInitialValue("all-TUs");
Julie Hockette975a472018-03-22 23:34:46 +0000171 auto Exec = clang::tooling::createExecutorFromCommandLineArgs(
172 argc, argv, ClangDocCategory);
173
174 if (!Exec) {
175 llvm::errs() << toString(Exec.takeError()) << "\n";
176 return 1;
177 }
178
Julie Hockettac68cab2018-09-11 15:56:55 +0000179 // Fail early if an invalid format was provided.
180 std::string Format = getFormatString();
181 llvm::outs() << "Emiting docs in " << Format << " format.\n";
182 auto G = doc::findGeneratorByName(Format);
183 if (!G) {
184 llvm::errs() << toString(G.takeError()) << "\n";
185 return 1;
186 }
187
Julie Hockette975a472018-03-22 23:34:46 +0000188 ArgumentsAdjuster ArgAdjuster;
189 if (!DoxygenOnly)
190 ArgAdjuster = combineAdjusters(
191 getInsertArgumentAdjuster("-fparse-all-comments",
192 tooling::ArgumentInsertPosition::END),
193 ArgAdjuster);
194
195 // Mapping phase
196 llvm::outs() << "Mapping decls...\n";
Julie Hocketteb50a2e2018-07-20 18:49:55 +0000197 clang::doc::ClangDocContext CDCtx = {Exec->get()->getExecutionContext(),
198 PublicOnly};
199 auto Err =
200 Exec->get()->execute(doc::newMapperActionFactory(CDCtx), ArgAdjuster);
Julie Hockettd0f9a872018-06-04 17:22:20 +0000201 if (Err) {
Julie Hockette975a472018-03-22 23:34:46 +0000202 llvm::errs() << toString(std::move(Err)) << "\n";
Julie Hockettd0f9a872018-06-04 17:22:20 +0000203 return 1;
204 }
Julie Hockette975a472018-03-22 23:34:46 +0000205
Julie Hockettd0f9a872018-06-04 17:22:20 +0000206 // Collect values into output by key.
Julie Hockettd0f9a872018-06-04 17:22:20 +0000207 // In ToolResults, the Key is the hashed USR and the value is the
208 // bitcode-encoded representation of the Info object.
Julie Hockett8899c292018-08-02 20:10:17 +0000209 llvm::outs() << "Collecting infos...\n";
210 llvm::StringMap<std::vector<std::unique_ptr<doc::Info>>> USRToInfos;
211 if (bitcodeResultsToInfos(*Exec->get()->getToolResults(), USRToInfos))
212 return 1;
Julie Hocketta9cb2dd2018-08-02 18:01:37 +0000213
Julie Hockett8899c292018-08-02 20:10:17 +0000214 // First reducing phase (reduce all decls into one info per decl).
215 llvm::outs() << "Reducing " << USRToInfos.size() << " infos...\n";
216 for (auto &Group : USRToInfos) {
Julie Hocketta9cb2dd2018-08-02 18:01:37 +0000217 auto Reduced = doc::mergeInfos(Group.getValue());
Julie Hockett8899c292018-08-02 20:10:17 +0000218 if (!Reduced) {
Julie Hocketta9cb2dd2018-08-02 18:01:37 +0000219 llvm::errs() << llvm::toString(Reduced.takeError());
Julie Hockett8899c292018-08-02 20:10:17 +0000220 continue;
221 }
Julie Hockettd0f9a872018-06-04 17:22:20 +0000222
Julie Hocketta9cb2dd2018-08-02 18:01:37 +0000223 doc::Info *I = Reduced.get().get();
Julie Hockett8899c292018-08-02 20:10:17 +0000224
225 auto InfoPath =
226 getInfoOutputFile(OutDirectory, I->Namespace, I->Name, "." + Format);
Julie Hockette78f3012018-06-06 16:13:17 +0000227 if (!InfoPath) {
228 llvm::errs() << toString(InfoPath.takeError()) << "\n";
229 continue;
230 }
231 std::error_code FileErr;
232 llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr, llvm::sys::fs::F_None);
233 if (FileErr != OK) {
Julie Hockett8899c292018-08-02 20:10:17 +0000234 llvm::errs() << "Error opening info file: " << FileErr.message() << "\n";
Julie Hockette78f3012018-06-06 16:13:17 +0000235 continue;
236 }
Julie Hockettd0f9a872018-06-04 17:22:20 +0000237
Julie Hockettac68cab2018-09-11 15:56:55 +0000238 if (auto Err = G->get()->generateDocForInfo(I, InfoOS))
239 llvm::errs() << toString(std::move(Err)) << "\n";
Julie Hockette975a472018-03-22 23:34:46 +0000240 }
241
242 return 0;
243}