clang-tools  7.0.0
ClangDocMain.cpp
Go to the documentation of this file.
1 //===-- ClangDocMain.cpp - ClangDoc -----------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This tool for generating C and C++ documenation from source code
11 // and comments. Generally, it runs a LibTooling FrontendAction on source files,
12 // mapping each declaration in those files to its USR and serializing relevant
13 // information into LLVM bitcode. It then runs a pass over the collected
14 // declaration information, reducing by USR. There is an option to dump this
15 // intermediate result to bitcode. Finally, it hands the reduced information
16 // off to a generator, which does the final parsing from the intermediate
17 // representation to the desired output format.
18 //
19 //===----------------------------------------------------------------------===//
20 
21 #include "BitcodeReader.h"
22 #include "BitcodeWriter.h"
23 #include "ClangDoc.h"
24 #include "Generators.h"
25 #include "Representation.h"
26 #include "clang/AST/AST.h"
27 #include "clang/AST/Decl.h"
28 #include "clang/ASTMatchers/ASTMatchFinder.h"
29 #include "clang/ASTMatchers/ASTMatchersInternal.h"
30 #include "clang/Driver/Options.h"
31 #include "clang/Frontend/FrontendActions.h"
32 #include "clang/Tooling/CommonOptionsParser.h"
33 #include "clang/Tooling/Execution.h"
34 #include "clang/Tooling/StandaloneExecution.h"
35 #include "clang/Tooling/Tooling.h"
36 #include "llvm/ADT/APFloat.h"
37 #include "llvm/Support/Error.h"
38 #include "llvm/Support/FileSystem.h"
39 #include "llvm/Support/Path.h"
40 #include "llvm/Support/Process.h"
41 #include "llvm/Support/Signals.h"
42 #include "llvm/Support/raw_ostream.h"
43 #include <string>
44 
45 using namespace clang::ast_matchers;
46 using namespace clang::tooling;
47 using namespace clang;
48 
49 static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
50 static llvm::cl::OptionCategory ClangDocCategory("clang-doc options");
51 
52 static llvm::cl::opt<std::string>
53  OutDirectory("output",
54  llvm::cl::desc("Directory for outputting generated files."),
55  llvm::cl::init("docs"), llvm::cl::cat(ClangDocCategory));
56 
57 static llvm::cl::opt<bool>
58  DumpMapperResult("dump-mapper",
59  llvm::cl::desc("Dump mapper results to bitcode file."),
60  llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
61 
62 static llvm::cl::opt<bool> DumpIntermediateResult(
63  "dump-intermediate",
64  llvm::cl::desc("Dump intermediate results to bitcode file."),
65  llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
66 
67 static llvm::cl::opt<bool>
68  PublicOnly("public", llvm::cl::desc("Document only public declarations."),
69  llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
70 
73 };
74 
75 static llvm::cl::opt<OutputFormatTy> FormatEnum(
76  "format", llvm::cl::desc("Format for outputted docs."),
77  llvm::cl::values(clEnumVal(yaml, "Documentation in YAML format.")),
78  llvm::cl::init(yaml), llvm::cl::cat(ClangDocCategory));
79 
80 static llvm::cl::opt<bool> DoxygenOnly(
81  "doxygen",
82  llvm::cl::desc("Use only doxygen-style comments to generate docs."),
83  llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
84 
85 bool CreateDirectory(const Twine &DirName, bool ClearDirectory = false) {
86  std::error_code OK;
87  llvm::SmallString<128> DocsRootPath;
88  if (ClearDirectory) {
89  std::error_code RemoveStatus = llvm::sys::fs::remove_directories(DirName);
90  if (RemoveStatus != OK) {
91  llvm::errs() << "Unable to remove existing documentation directory for "
92  << DirName << ".\n";
93  return true;
94  }
95  }
96  std::error_code DirectoryStatus = llvm::sys::fs::create_directories(DirName);
97  if (DirectoryStatus != OK) {
98  llvm::errs() << "Unable to create documentation directories.\n";
99  return true;
100  }
101  return false;
102 }
103 
104 bool DumpResultToFile(const Twine &DirName, const Twine &FileName,
105  StringRef Buffer, bool ClearDirectory = false) {
106  std::error_code OK;
107  llvm::SmallString<128> IRRootPath;
108  llvm::sys::path::native(OutDirectory, IRRootPath);
109  llvm::sys::path::append(IRRootPath, DirName);
110  if (CreateDirectory(IRRootPath, ClearDirectory))
111  return true;
112  llvm::sys::path::append(IRRootPath, FileName);
113  std::error_code OutErrorInfo;
114  llvm::raw_fd_ostream OS(IRRootPath, OutErrorInfo, llvm::sys::fs::F_None);
115  if (OutErrorInfo != OK) {
116  llvm::errs() << "Error opening documentation file.\n";
117  return true;
118  }
119  OS << Buffer;
120  OS.close();
121  return false;
122 }
123 
124 llvm::Expected<llvm::SmallString<128>>
125 getPath(StringRef Root, StringRef Ext, StringRef Name,
126  llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
127  std::error_code OK;
128  llvm::SmallString<128> Path;
129  llvm::sys::path::native(Root, Path);
130  for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
131  llvm::sys::path::append(Path, R->Name);
132 
133  if (CreateDirectory(Path))
134  return llvm::make_error<llvm::StringError>("Unable to create directory.\n",
135  llvm::inconvertibleErrorCode());
136 
137  llvm::sys::path::append(Path, Name + Ext);
138  return Path;
139 }
140 
142  switch (Ty) {
143  case yaml:
144  return "yaml";
145  }
146  llvm_unreachable("Unknown OutputFormatTy");
147 }
148 
149 int main(int argc, const char **argv) {
150  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
151  std::error_code OK;
152 
153  // Fail early if an invalid format was provided.
154  std::string Format = getFormatString(FormatEnum);
155  auto G = doc::findGeneratorByName(Format);
156  if (!G) {
157  llvm::errs() << toString(G.takeError()) << "\n";
158  return 1;
159  }
160 
161  auto Exec = clang::tooling::createExecutorFromCommandLineArgs(
162  argc, argv, ClangDocCategory);
163 
164  if (!Exec) {
165  llvm::errs() << toString(Exec.takeError()) << "\n";
166  return 1;
167  }
168 
169  ArgumentsAdjuster ArgAdjuster;
170  if (!DoxygenOnly)
171  ArgAdjuster = combineAdjusters(
172  getInsertArgumentAdjuster("-fparse-all-comments",
173  tooling::ArgumentInsertPosition::END),
174  ArgAdjuster);
175 
176  // Mapping phase
177  llvm::outs() << "Mapping decls...\n";
178  clang::doc::ClangDocContext CDCtx = {Exec->get()->getExecutionContext(),
179  PublicOnly};
180  auto Err =
181  Exec->get()->execute(doc::newMapperActionFactory(CDCtx), ArgAdjuster);
182  if (Err) {
183  llvm::errs() << toString(std::move(Err)) << "\n";
184  return 1;
185  }
186 
187  if (DumpMapperResult) {
188  bool Err = false;
189  Exec->get()->getToolResults()->forEachResult(
190  [&](StringRef Key, StringRef Value) {
191  Err = DumpResultToFile("bc", Key + ".bc", Value);
192  });
193  if (Err)
194  llvm::errs() << "Error dumping map results.\n";
195  return Err;
196  }
197 
198  // Collect values into output by key.
199  llvm::outs() << "Collecting infos...\n";
200  llvm::StringMap<std::vector<std::unique_ptr<doc::Info>>> MapOutput;
201 
202  // In ToolResults, the Key is the hashed USR and the value is the
203  // bitcode-encoded representation of the Info object.
204  Exec->get()->getToolResults()->forEachResult([&](StringRef Key,
205  StringRef Value) {
206  llvm::BitstreamCursor Stream(Value);
207  doc::ClangDocBitcodeReader Reader(Stream);
208  auto Infos = Reader.readBitcode();
209  for (auto &I : Infos) {
210  auto R =
211  MapOutput.try_emplace(Key, std::vector<std::unique_ptr<doc::Info>>());
212  R.first->second.emplace_back(std::move(I));
213  }
214  });
215 
216  // Reducing and generation phases
217  llvm::outs() << "Reducing " << MapOutput.size() << " infos...\n";
218  llvm::StringMap<std::unique_ptr<doc::Info>> ReduceOutput;
219  for (auto &Group : MapOutput) {
220  auto Reduced = doc::mergeInfos(Group.getValue());
221  if (!Reduced)
222  llvm::errs() << llvm::toString(Reduced.takeError());
223 
225  SmallString<4096> Buffer;
226  llvm::BitstreamWriter Stream(Buffer);
227  doc::ClangDocBitcodeWriter Writer(Stream);
228  Writer.dispatchInfoForWrite(Reduced.get().get());
229  if (DumpResultToFile("bc", Group.getKey() + ".bc", Buffer))
230  llvm::errs() << "Error dumping to bitcode.\n";
231  continue;
232  }
233 
234  // Create the relevant ostream and emit the documentation for this decl.
235  doc::Info *I = Reduced.get().get();
236  auto InfoPath = getPath(OutDirectory, "." + Format, I->Name, I->Namespace);
237  if (!InfoPath) {
238  llvm::errs() << toString(InfoPath.takeError()) << "\n";
239  continue;
240  }
241  std::error_code FileErr;
242  llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr, llvm::sys::fs::F_None);
243  if (FileErr != OK) {
244  llvm::errs() << "Error opening index file: " << FileErr.message() << "\n";
245  continue;
246  }
247 
248  if (G->get()->generateDocForInfo(I, InfoOS))
249  llvm::errs() << "Unable to generate docs for info.\n";
250  }
251 
252  return 0;
253 }
llvm::StringRef Name
llvm::SmallVector< Reference, 4 > Namespace
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
std::string getFormatString(OutputFormatTy Ty)
bool CreateDirectory(const Twine &DirName, bool ClearDirectory=false)
std::vector< HeaderHandle > Path
static llvm::cl::opt< bool > PublicOnly("public", llvm::cl::desc("Document only public declarations."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory))
llvm::Expected< std::unique_ptr< Generator > > findGeneratorByName(llvm::StringRef Format)
Definition: Generators.cpp:18
static llvm::cl::opt< std::string > OutDirectory("output", llvm::cl::desc("Directory for outputting generated files."), llvm::cl::init("docs"), llvm::cl::cat(ClangDocCategory))
static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage)
int main(int argc, const char **argv)
static llvm::cl::opt< bool > DoxygenOnly("doxygen", llvm::cl::desc("Use only doxygen-style comments to generate docs."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory))
llvm::Expected< std::unique_ptr< Info > > mergeInfos(std::vector< std::unique_ptr< Info >> &Values)
SmallString< 16 > Name
PathRef FileName
A base struct for Infos.
llvm::Expected< llvm::SmallString< 128 > > getPath(StringRef Root, StringRef Ext, StringRef Name, llvm::SmallVectorImpl< doc::Reference > &Namespaces)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
bool DumpResultToFile(const Twine &DirName, const Twine &FileName, StringRef Buffer, bool ClearDirectory=false)
static llvm::cl::opt< bool > DumpMapperResult("dump-mapper", llvm::cl::desc("Dump mapper results to bitcode file."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory))
OutputFormatTy
static llvm::cl::opt< OutputFormatTy > FormatEnum("format", llvm::cl::desc("Format for outputted docs."), llvm::cl::values(clEnumVal(yaml, "Documentation in YAML format.")), llvm::cl::init(yaml), llvm::cl::cat(ClangDocCategory))
static llvm::cl::OptionCategory ClangDocCategory("clang-doc options")
static llvm::cl::opt< bool > DumpIntermediateResult("dump-intermediate", llvm::cl::desc("Dump intermediate results to bitcode file."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory))
std::unique_ptr< tooling::FrontendActionFactory > newMapperActionFactory(ClangDocContext CDCtx)
Definition: ClangDoc.cpp:57
std::vector< std::unique_ptr< Info > > readBitcode()