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