clang-tools 22.0.0git
HTMLMustacheGenerator.cpp
Go to the documentation of this file.
1///===----------------------------------------------------------------------===//
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/// \file
10/// This file contains the implementation of the MustacheHTMLGenerator class,
11/// which is Clang-Doc generator for HTML using Mustache templates.
12///
13//===----------------------------------------------------------------------===//
14
15#include "Generators.h"
16#include "Representation.h"
17#include "support/File.h"
18#include "llvm/Support/Error.h"
19#include "llvm/Support/MemoryBuffer.h"
20#include "llvm/Support/Mustache.h"
21#include "llvm/Support/Path.h"
22#include "llvm/Support/TimeProfiler.h"
23
24using namespace llvm;
25using namespace llvm::json;
26using namespace llvm::mustache;
27
28namespace clang {
29namespace doc {
30static Error generateDocForJSON(json::Value &JSON, StringRef Filename,
31 StringRef Path, raw_fd_ostream &OS,
32 const ClangDocContext &CDCtx);
33
34static Error createFileOpenError(StringRef FileName, std::error_code EC) {
35 return createFileError("cannot open file " + FileName, EC);
36}
37
39public:
40 static const char *Format;
41 Error generateDocs(StringRef RootDir,
42 StringMap<std::unique_ptr<doc::Info>> Infos,
43 const ClangDocContext &CDCtx) override;
44 Error createResources(ClangDocContext &CDCtx) override;
45 Error generateDocForInfo(Info *I, raw_ostream &OS,
46 const ClangDocContext &CDCtx) override;
47};
48
50public:
51 static Expected<std::unique_ptr<MustacheTemplateFile>>
52 createMustacheFile(StringRef FileName) {
53 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
54 MemoryBuffer::getFile(FileName);
55 if (auto EC = BufferOrError.getError())
56 return createFileOpenError(FileName, EC);
57
58 std::unique_ptr<MemoryBuffer> Buffer = std::move(BufferOrError.get());
59 StringRef FileContent = Buffer->getBuffer();
60 return std::make_unique<MustacheTemplateFile>(FileContent);
61 }
62
63 Error registerPartialFile(StringRef Name, StringRef FileName) {
64 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
65 MemoryBuffer::getFile(FileName);
66 if (auto EC = BufferOrError.getError())
67 return createFileOpenError(FileName, EC);
68
69 std::unique_ptr<MemoryBuffer> Buffer = std::move(BufferOrError.get());
70 StringRef FileContent = Buffer->getBuffer();
71 registerPartial(Name.str(), FileContent.str());
72 return Error::success();
73 }
74
75 MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
76};
77
78static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
79
80static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
81
82static Error
83setupTemplate(std::unique_ptr<MustacheTemplateFile> &Template,
84 StringRef TemplatePath,
85 std::vector<std::pair<StringRef, StringRef>> Partials) {
86 auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
87 if (Error Err = T.takeError())
88 return Err;
89 Template = std::move(T.get());
90 for (const auto &[Name, FileName] : Partials)
91 if (auto Err = Template->registerPartialFile(Name, FileName))
92 return Err;
93 return Error::success();
94}
95
97 // Template files need to use the native path when they're opened,
98 // but have to be used in POSIX style when used in HTML.
99 auto ConvertToNative = [](std::string &&Path) -> std::string {
100 SmallString<128> PathBuf(Path);
101 llvm::sys::path::native(PathBuf);
102 return PathBuf.str().str();
103 };
104
105 std::string NamespaceFilePath =
106 ConvertToNative(CDCtx.MustacheTemplates.lookup("namespace-template"));
107 std::string ClassFilePath =
108 ConvertToNative(CDCtx.MustacheTemplates.lookup("class-template"));
109 std::string CommentFilePath =
110 ConvertToNative(CDCtx.MustacheTemplates.lookup("comment-template"));
111 std::string FunctionFilePath =
112 ConvertToNative(CDCtx.MustacheTemplates.lookup("function-template"));
113 std::string EnumFilePath =
114 ConvertToNative(CDCtx.MustacheTemplates.lookup("enum-template"));
115 std::vector<std::pair<StringRef, StringRef>> Partials = {
116 {"Comments", CommentFilePath},
117 {"FunctionPartial", FunctionFilePath},
118 {"EnumPartial", EnumFilePath}};
119
120 if (Error Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials))
121 return Err;
122
123 if (Error Err = setupTemplate(RecordTemplate, ClassFilePath, Partials))
124 return Err;
125
126 return Error::success();
127}
128
130 StringRef RootDir, StringMap<std::unique_ptr<doc::Info>> Infos,
131 const clang::doc::ClangDocContext &CDCtx) {
132 {
133 llvm::TimeTraceScope TS("Setup Templates");
134 if (auto Err = setupTemplateFiles(CDCtx))
135 return Err;
136 }
137
138 {
139 llvm::TimeTraceScope TS("Generate JSON for Mustache");
140 if (auto JSONGenerator = findGeneratorByName("json")) {
141 if (Error Err = JSONGenerator.get()->generateDocs(
142 RootDir, std::move(Infos), CDCtx))
143 return Err;
144 } else
145 return JSONGenerator.takeError();
146 }
147 SmallString<128> JSONPath;
148 sys::path::native(RootDir.str() + "/json", JSONPath);
149
150 StringMap<json::Value> JSONFileMap;
151 {
152 llvm::TimeTraceScope TS("Iterate JSON files");
153 std::error_code EC;
154 sys::fs::directory_iterator JSONIter(JSONPath, EC);
155 std::vector<json::Value> JSONFiles;
156 JSONFiles.reserve(Infos.size());
157 if (EC)
158 return createStringError("Failed to create directory iterator.");
159
160 SmallString<128> HTMLDirPath(RootDir.str() + "/html/");
161 if (auto EC = sys::fs::create_directories(HTMLDirPath))
162 return createFileError(HTMLDirPath, EC);
163 while (JSONIter != sys::fs::directory_iterator()) {
164 if (EC)
165 return createFileError("Failed to iterate: " + JSONIter->path(), EC);
166
167 auto Path = StringRef(JSONIter->path());
168 if (!Path.ends_with(".json")) {
169 JSONIter.increment(EC);
170 continue;
171 }
172
173 auto File = MemoryBuffer::getFile(Path);
174 if (EC = File.getError(); EC)
175 // TODO: Buffer errors to report later, look into using Clang
176 // diagnostics.
177 llvm::errs() << "Failed to open file: " << Path << " " << EC.message()
178 << '\n';
179
180 auto Parsed = json::parse((*File)->getBuffer());
181 if (!Parsed)
182 return Parsed.takeError();
183
184 std::error_code FileErr;
185 SmallString<128> HTMLFilePath(HTMLDirPath);
186 sys::path::append(HTMLFilePath, sys::path::filename(Path));
187 sys::path::replace_extension(HTMLFilePath, "html");
188 raw_fd_ostream InfoOS(HTMLFilePath, FileErr, sys::fs::OF_None);
189 if (FileErr)
190 return createFileOpenError(Path, FileErr);
191
192 if (Error Err = generateDocForJSON(*Parsed, sys::path::stem(HTMLFilePath),
193 HTMLFilePath, InfoOS, CDCtx))
194 return Err;
195 JSONIter.increment(EC);
196 }
197 }
198
199 return Error::success();
200}
201
202static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V) {
203 V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
204 json::Value StylesheetArr = Array();
205 SmallString<128> RelativePath("./");
206 sys::path::native(RelativePath, sys::path::Style::posix);
207
208 auto *SSA = StylesheetArr.getAsArray();
209 SSA->reserve(CDCtx.UserStylesheets.size());
210 for (const auto &FilePath : CDCtx.UserStylesheets) {
211 SmallString<128> StylesheetPath = RelativePath;
212 sys::path::append(StylesheetPath, sys::path::Style::posix,
213 sys::path::filename(FilePath));
214 SSA->emplace_back(StylesheetPath);
215 }
216 V.getAsObject()->insert({"Stylesheets", StylesheetArr});
217
218 json::Value ScriptArr = Array();
219 auto *SCA = ScriptArr.getAsArray();
220 SCA->reserve(CDCtx.JsScripts.size());
221 for (auto Script : CDCtx.JsScripts) {
222 SmallString<128> JsPath = RelativePath;
223 sys::path::append(JsPath, sys::path::Style::posix,
224 sys::path::filename(Script));
225 SCA->emplace_back(JsPath);
226 }
227 V.getAsObject()->insert({"Scripts", ScriptArr});
228 return Error::success();
229}
230
231static Error generateDocForJSON(json::Value &JSON, StringRef Filename,
232 StringRef Path, raw_fd_ostream &OS,
233 const ClangDocContext &CDCtx) {
234 auto StrValue = (*JSON.getAsObject())["InfoType"];
235 if (StrValue.kind() != json::Value::Kind::String)
236 return createStringError("JSON file '%s' does not contain key: 'InfoType'.",
237 Filename.str().c_str());
238 auto ObjTypeStr = StrValue.getAsString();
239 if (!ObjTypeStr.has_value())
240 return createStringError(
241 "JSON file '%s' does not contain 'InfoType' field as a string.",
242 Filename.str().c_str());
243
244 if (ObjTypeStr.value() == "namespace") {
245 if (auto Err = setupTemplateValue(CDCtx, JSON))
246 return Err;
247 assert(NamespaceTemplate && "NamespaceTemplate is nullptr.");
248 NamespaceTemplate->render(JSON, OS);
249 } else if (ObjTypeStr.value() == "record") {
250 if (auto Err = setupTemplateValue(CDCtx, JSON))
251 return Err;
252 assert(RecordTemplate && "RecordTemplate is nullptr.");
253 RecordTemplate->render(JSON, OS);
254 }
255 return Error::success();
256}
257
259 const ClangDocContext &CDCtx) {
260 switch (I->IT) {
269 break;
271 return createStringError(inconvertibleErrorCode(), "unexpected InfoType");
272 }
273 return Error::success();
274}
275
277 for (const auto &FilePath : CDCtx.UserStylesheets)
278 if (Error Err = copyFile(FilePath, CDCtx.OutDirectory))
279 return Err;
280 for (const auto &FilePath : CDCtx.JsScripts)
281 if (Error Err = copyFile(FilePath, CDCtx.OutDirectory))
282 return Err;
283 return Error::success();
284}
285
286const char *MustacheHTMLGenerator::Format = "mustache";
287
288static GeneratorRegistry::Add<MustacheHTMLGenerator>
289 MHTML(MustacheHTMLGenerator::Format, "Generator for mustache HTML output.");
290
291// This anchor is used to force the linker to link in the generated object
292// file and thus register the generator.
294
295} // namespace doc
296} // namespace clang
Error generateDocs(StringRef RootDir, llvm::StringMap< std::unique_ptr< doc::Info > > Infos, const ClangDocContext &CDCtx) override
Error createResources(ClangDocContext &CDCtx) override
Error generateDocs(StringRef RootDir, StringMap< std::unique_ptr< doc::Info > > Infos, const ClangDocContext &CDCtx) override
Error generateDocForInfo(Info *I, raw_ostream &OS, const ClangDocContext &CDCtx) override
static Expected< std::unique_ptr< MustacheTemplateFile > > createMustacheFile(StringRef FileName)
Error registerPartialFile(StringRef Name, StringRef FileName)
@ Error
An error message.
Definition Protocol.h:734
llvm::Error copyFile(llvm::StringRef FilePath, llvm::StringRef OutDirectory)
Definition File.cpp:15
static Error setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx)
static Error setupTemplate(std::unique_ptr< MustacheTemplateFile > &Template, StringRef TemplatePath, std::vector< std::pair< StringRef, StringRef > > Partials)
llvm::Expected< std::unique_ptr< Generator > > findGeneratorByName(llvm::StringRef Format)
static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V)
static std::unique_ptr< MustacheTemplateFile > RecordTemplate
static GeneratorRegistry::Add< JSONGenerator > JSON(JSONGenerator::Format, "Generator for JSON output.")
static Error generateDocForJSON(json::Value &JSON, StringRef Filename, StringRef Path, raw_fd_ostream &OS, const ClangDocContext &CDCtx)
static GeneratorRegistry::Add< MustacheHTMLGenerator > MHTML(MustacheHTMLGenerator::Format, "Generator for mustache HTML output.")
static std::unique_ptr< MustacheTemplateFile > NamespaceTemplate
static Error createFileOpenError(StringRef FileName, std::error_code EC)
volatile int MHTMLGeneratorAnchorSource
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
Definition Generators.h:66
std::vector< std::string > UserStylesheets
llvm::StringMap< std::string > MustacheTemplates
std::vector< std::string > JsScripts
A base struct for Infos.