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
50 BumpPtrAllocator Allocator;
51 StringSaver Saver;
52 MustacheContext Ctx;
53 Template T;
54 std::unique_ptr<MemoryBuffer> Buffer;
55
56public:
57 static Expected<std::unique_ptr<MustacheTemplateFile>>
58 createMustacheFile(StringRef FileName) {
59 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
60 MemoryBuffer::getFile(FileName);
61 if (auto EC = BufferOrError.getError())
62 return createFileOpenError(FileName, EC);
63 return std::make_unique<MustacheTemplateFile>(
64 std::move(BufferOrError.get()));
65 }
66
67 Error registerPartialFile(StringRef Name, StringRef FileName) {
68 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
69 MemoryBuffer::getFile(FileName);
70 if (auto EC = BufferOrError.getError())
71 return createFileOpenError(FileName, EC);
72
73 std::unique_ptr<MemoryBuffer> Buffer = std::move(BufferOrError.get());
74 StringRef FileContent = Buffer->getBuffer();
75 T.registerPartial(Name.str(), FileContent.str());
76 return Error::success();
77 }
78
79 void render(json::Value &V, raw_ostream &OS) { T.render(V, OS); }
80
81 MustacheTemplateFile(std::unique_ptr<MemoryBuffer> &&B)
82 : Saver(Allocator), Ctx(Allocator, Saver), T(B->getBuffer(), Ctx),
83 Buffer(std::move(B)) {}
84};
85
86static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
87
88static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
89
90static Error
91setupTemplate(std::unique_ptr<MustacheTemplateFile> &Template,
92 StringRef TemplatePath,
93 std::vector<std::pair<StringRef, StringRef>> Partials) {
94 auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
95 if (Error Err = T.takeError())
96 return Err;
97 Template = std::move(T.get());
98 for (const auto &[Name, FileName] : Partials)
99 if (auto Err = Template->registerPartialFile(Name, FileName))
100 return Err;
101 return Error::success();
102}
103
105 // Template files need to use the native path when they're opened,
106 // but have to be used in POSIX style when used in HTML.
107 auto ConvertToNative = [](std::string &&Path) -> std::string {
108 SmallString<128> PathBuf(Path);
109 llvm::sys::path::native(PathBuf);
110 return PathBuf.str().str();
111 };
112
113 std::string NamespaceFilePath =
114 ConvertToNative(CDCtx.MustacheTemplates.lookup("namespace-template"));
115 std::string ClassFilePath =
116 ConvertToNative(CDCtx.MustacheTemplates.lookup("class-template"));
117 std::string CommentFilePath =
118 ConvertToNative(CDCtx.MustacheTemplates.lookup("comment-template"));
119 std::string FunctionFilePath =
120 ConvertToNative(CDCtx.MustacheTemplates.lookup("function-template"));
121 std::string EnumFilePath =
122 ConvertToNative(CDCtx.MustacheTemplates.lookup("enum-template"));
123 std::vector<std::pair<StringRef, StringRef>> Partials = {
124 {"Comments", CommentFilePath},
125 {"FunctionPartial", FunctionFilePath},
126 {"EnumPartial", EnumFilePath}};
127
128 if (Error Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials))
129 return Err;
130
131 if (Error Err = setupTemplate(RecordTemplate, ClassFilePath, Partials))
132 return Err;
133
134 return Error::success();
135}
136
138 StringRef RootDir, StringMap<std::unique_ptr<doc::Info>> Infos,
139 const clang::doc::ClangDocContext &CDCtx) {
140 {
141 llvm::TimeTraceScope TS("Setup Templates");
142 if (auto Err = setupTemplateFiles(CDCtx))
143 return Err;
144 }
145
146 {
147 llvm::TimeTraceScope TS("Generate JSON for Mustache");
148 if (auto JSONGenerator = findGeneratorByName("json")) {
149 if (Error Err = JSONGenerator.get()->generateDocs(
150 RootDir, std::move(Infos), CDCtx))
151 return Err;
152 } else
153 return JSONGenerator.takeError();
154 }
155 SmallString<128> JSONPath;
156 sys::path::native(RootDir.str() + "/json", JSONPath);
157
158 StringMap<json::Value> JSONFileMap;
159 {
160 llvm::TimeTraceScope TS("Iterate JSON files");
161 std::error_code EC;
162 sys::fs::directory_iterator JSONIter(JSONPath, EC);
163 std::vector<json::Value> JSONFiles;
164 JSONFiles.reserve(Infos.size());
165 if (EC)
166 return createStringError("Failed to create directory iterator.");
167
168 SmallString<128> HTMLDirPath(RootDir.str() + "/html/");
169 if (auto EC = sys::fs::create_directories(HTMLDirPath))
170 return createFileError(HTMLDirPath, EC);
171 while (JSONIter != sys::fs::directory_iterator()) {
172 if (EC)
173 return createFileError("Failed to iterate: " + JSONIter->path(), EC);
174
175 auto Path = StringRef(JSONIter->path());
176 if (!Path.ends_with(".json")) {
177 JSONIter.increment(EC);
178 continue;
179 }
180
181 auto File = MemoryBuffer::getFile(Path);
182 if (EC = File.getError(); EC)
183 // TODO: Buffer errors to report later, look into using Clang
184 // diagnostics.
185 llvm::errs() << "Failed to open file: " << Path << " " << EC.message()
186 << '\n';
187
188 auto Parsed = json::parse((*File)->getBuffer());
189 if (!Parsed)
190 return Parsed.takeError();
191
192 std::error_code FileErr;
193 SmallString<128> HTMLFilePath(HTMLDirPath);
194 sys::path::append(HTMLFilePath, sys::path::filename(Path));
195 sys::path::replace_extension(HTMLFilePath, "html");
196 raw_fd_ostream InfoOS(HTMLFilePath, FileErr, sys::fs::OF_None);
197 if (FileErr)
198 return createFileOpenError(Path, FileErr);
199
200 if (Error Err = generateDocForJSON(*Parsed, sys::path::stem(HTMLFilePath),
201 HTMLFilePath, InfoOS, CDCtx))
202 return Err;
203 JSONIter.increment(EC);
204 }
205 }
206
207 return Error::success();
208}
209
210static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V) {
211 V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
212 json::Value StylesheetArr = Array();
213 SmallString<128> RelativePath("./");
214 sys::path::native(RelativePath, sys::path::Style::posix);
215
216 auto *SSA = StylesheetArr.getAsArray();
217 SSA->reserve(CDCtx.UserStylesheets.size());
218 for (const auto &FilePath : CDCtx.UserStylesheets) {
219 SmallString<128> StylesheetPath = RelativePath;
220 sys::path::append(StylesheetPath, sys::path::Style::posix,
221 sys::path::filename(FilePath));
222 SSA->emplace_back(StylesheetPath);
223 }
224 V.getAsObject()->insert({"Stylesheets", StylesheetArr});
225
226 json::Value ScriptArr = Array();
227 auto *SCA = ScriptArr.getAsArray();
228 SCA->reserve(CDCtx.JsScripts.size());
229 for (auto Script : CDCtx.JsScripts) {
230 SmallString<128> JsPath = RelativePath;
231 sys::path::append(JsPath, sys::path::Style::posix,
232 sys::path::filename(Script));
233 SCA->emplace_back(JsPath);
234 }
235 V.getAsObject()->insert({"Scripts", ScriptArr});
236 return Error::success();
237}
238
239static Error generateDocForJSON(json::Value &JSON, StringRef Filename,
240 StringRef Path, raw_fd_ostream &OS,
241 const ClangDocContext &CDCtx) {
242 auto StrValue = (*JSON.getAsObject())["InfoType"];
243 if (StrValue.kind() != json::Value::Kind::String)
244 return createStringError("JSON file '%s' does not contain key: 'InfoType'.",
245 Filename.str().c_str());
246 auto ObjTypeStr = StrValue.getAsString();
247 if (!ObjTypeStr.has_value())
248 return createStringError(
249 "JSON file '%s' does not contain 'InfoType' field as a string.",
250 Filename.str().c_str());
251
252 if (ObjTypeStr.value() == "namespace") {
253 if (auto Err = setupTemplateValue(CDCtx, JSON))
254 return Err;
255 assert(NamespaceTemplate && "NamespaceTemplate is nullptr.");
256 NamespaceTemplate->render(JSON, OS);
257 } else if (ObjTypeStr.value() == "record") {
258 if (auto Err = setupTemplateValue(CDCtx, JSON))
259 return Err;
260 assert(RecordTemplate && "RecordTemplate is nullptr.");
261 RecordTemplate->render(JSON, OS);
262 }
263 return Error::success();
264}
265
267 const ClangDocContext &CDCtx) {
268 switch (I->IT) {
277 break;
279 return createStringError(inconvertibleErrorCode(), "unexpected InfoType");
280 }
281 return Error::success();
282}
283
285 std::string ResourcePath(CDCtx.OutDirectory + "/html");
286 for (const auto &FilePath : CDCtx.UserStylesheets)
287 if (Error Err = copyFile(FilePath, ResourcePath))
288 return Err;
289 for (const auto &FilePath : CDCtx.JsScripts)
290 if (Error Err = copyFile(FilePath, ResourcePath))
291 return Err;
292 return Error::success();
293}
294
295const char *MustacheHTMLGenerator::Format = "mustache";
296
297static GeneratorRegistry::Add<MustacheHTMLGenerator>
298 MHTML(MustacheHTMLGenerator::Format, "Generator for mustache HTML output.");
299
300// This anchor is used to force the linker to link in the generated object
301// file and thus register the generator.
303
304} // namespace doc
305} // 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)
void render(json::Value &V, raw_ostream &OS)
MustacheTemplateFile(std::unique_ptr< MemoryBuffer > &&B)
@ 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.