blob: 759342943e793780a8d42714c3404d7f9c6f4232 [file] [log] [blame]
Julie Hockette975a472018-03-22 23:34:46 +00001//===-- 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
Julie Hockettd0f9a872018-06-04 17:22:20 +000021#include "BitcodeReader.h"
22#include "BitcodeWriter.h"
Julie Hockette975a472018-03-22 23:34:46 +000023#include "ClangDoc.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"
33#include "clang/Tooling/StandaloneExecution.h"
34#include "clang/Tooling/Tooling.h"
35#include "llvm/ADT/APFloat.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>
57 DumpMapperResult("dump-mapper",
58 llvm::cl::desc("Dump mapper results to bitcode file."),
59 llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
60
Julie Hockettd0f9a872018-06-04 17:22:20 +000061static llvm::cl::opt<bool> DumpIntermediateResult(
62 "dump-intermediate",
63 llvm::cl::desc("Dump intermediate results to bitcode file."),
64 llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
65
66enum OutputFormatTy {
67 yaml,
68};
69
70static llvm::cl::opt<OutputFormatTy>
71 Format("format", llvm::cl::desc("Format for outputted docs."),
72 llvm::cl::values(clEnumVal(yaml, "Documentation in YAML format.")),
73 llvm::cl::init(yaml), llvm::cl::cat(ClangDocCategory));
74
Julie Hockette975a472018-03-22 23:34:46 +000075static llvm::cl::opt<bool> DoxygenOnly(
76 "doxygen",
77 llvm::cl::desc("Use only doxygen-style comments to generate docs."),
78 llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
79
Julie Hockettd0f9a872018-06-04 17:22:20 +000080bool CreateDirectory(const Twine &DirName, bool ClearDirectory = false) {
81 std::error_code OK;
82 llvm::SmallString<128> DocsRootPath;
83 if (ClearDirectory) {
84 std::error_code RemoveStatus = llvm::sys::fs::remove_directories(DirName);
85 if (RemoveStatus != OK) {
86 llvm::errs() << "Unable to remove existing documentation directory for "
87 << DirName << ".\n";
88 return true;
89 }
90 }
91 std::error_code DirectoryStatus = llvm::sys::fs::create_directories(DirName);
92 if (DirectoryStatus != OK) {
93 llvm::errs() << "Unable to create documentation directories.\n";
94 return true;
95 }
96 return false;
97}
98
99bool DumpResultToFile(const Twine &DirName, const Twine &FileName,
100 StringRef Buffer, bool ClearDirectory = false) {
101 std::error_code OK;
102 llvm::SmallString<128> IRRootPath;
103 llvm::sys::path::native(OutDirectory, IRRootPath);
104 llvm::sys::path::append(IRRootPath, DirName);
105 if (CreateDirectory(IRRootPath, ClearDirectory))
106 return true;
107 llvm::sys::path::append(IRRootPath, FileName);
108 std::error_code OutErrorInfo;
109 llvm::raw_fd_ostream OS(IRRootPath, OutErrorInfo, llvm::sys::fs::F_None);
110 if (OutErrorInfo != OK) {
111 llvm::errs() << "Error opening documentation file.\n";
112 return true;
113 }
114 OS << Buffer;
115 OS.close();
116 return false;
117}
118
Julie Hockette975a472018-03-22 23:34:46 +0000119int main(int argc, const char **argv) {
120 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
Julie Hockette975a472018-03-22 23:34:46 +0000121
122 auto Exec = clang::tooling::createExecutorFromCommandLineArgs(
123 argc, argv, ClangDocCategory);
124
125 if (!Exec) {
126 llvm::errs() << toString(Exec.takeError()) << "\n";
127 return 1;
128 }
129
130 ArgumentsAdjuster ArgAdjuster;
131 if (!DoxygenOnly)
132 ArgAdjuster = combineAdjusters(
133 getInsertArgumentAdjuster("-fparse-all-comments",
134 tooling::ArgumentInsertPosition::END),
135 ArgAdjuster);
136
137 // Mapping phase
138 llvm::outs() << "Mapping decls...\n";
Julie Hockettd0f9a872018-06-04 17:22:20 +0000139 auto Err = Exec->get()->execute(
140 doc::newMapperActionFactory(Exec->get()->getExecutionContext()),
141 ArgAdjuster);
142 if (Err) {
Julie Hockette975a472018-03-22 23:34:46 +0000143 llvm::errs() << toString(std::move(Err)) << "\n";
Julie Hockettd0f9a872018-06-04 17:22:20 +0000144 return 1;
145 }
Julie Hockette975a472018-03-22 23:34:46 +0000146
147 if (DumpMapperResult) {
Julie Hockettd0f9a872018-06-04 17:22:20 +0000148 bool Err = false;
149 Exec->get()->getToolResults()->forEachResult(
150 [&](StringRef Key, StringRef Value) {
151 Err = DumpResultToFile("bc", Key + ".bc", Value);
152 });
153 if (Err)
154 llvm::errs() << "Error dumping map results.\n";
155 return Err;
156 }
157
158 // Collect values into output by key.
159 llvm::outs() << "Collecting infos...\n";
160 llvm::StringMap<std::vector<std::unique_ptr<doc::Info>>> MapOutput;
161
162 // In ToolResults, the Key is the hashed USR and the value is the
163 // bitcode-encoded representation of the Info object.
164 Exec->get()->getToolResults()->forEachResult([&](StringRef Key,
165 StringRef Value) {
166 llvm::BitstreamCursor Stream(Value);
167 doc::ClangDocBitcodeReader Reader(Stream);
168 auto Infos = Reader.readBitcode();
169 for (auto &I : Infos) {
170 auto R =
171 MapOutput.try_emplace(Key, std::vector<std::unique_ptr<doc::Info>>());
172 R.first->second.emplace_back(std::move(I));
173 }
174 });
175
176 // Reducing phase
177 llvm::outs() << "Reducing " << MapOutput.size() << " infos...\n";
178 llvm::StringMap<std::unique_ptr<doc::Info>> ReduceOutput;
179 for (auto &Group : MapOutput) {
180 auto Reduced = doc::mergeInfos(Group.getValue());
181 if (!Reduced)
182 llvm::errs() << llvm::toString(Reduced.takeError());
183
184 if (DumpIntermediateResult) {
185 SmallString<4096> Buffer;
186 llvm::BitstreamWriter Stream(Buffer);
187 doc::ClangDocBitcodeWriter Writer(Stream);
188 Writer.dispatchInfoForWrite(Reduced.get().get());
189 if (DumpResultToFile("bc", Group.getKey() + ".bc", Buffer)) {
190 llvm::errs() << "Error writing " << Group.getKey() << " to file.\n";
191 continue;
Julie Hockette975a472018-03-22 23:34:46 +0000192 }
Julie Hockettd0f9a872018-06-04 17:22:20 +0000193 }
194
195 ReduceOutput.insert(
196 std::make_pair(Group.getKey(), std::move(Reduced.get())));
197
198 // FIXME: Add support for emitting different output formats.
Julie Hockette975a472018-03-22 23:34:46 +0000199 }
200
201 return 0;
202}